IOS多线程
ios有三种主要方法:1、NSThread。2、NSOperation。3、GCD。
1、NSThread:
调用方法如下:如果需要函数参数的话,可以通过Object传递。
1.1:NSThread*myThread=[[NSThreadalloc]initWithTarget:selfselector:@selector(threadInMainMethod:)object:nil];
[myThreadstart];
1.2:[NSThreaddetachNewThreadSelector:@selector(threadInMainMethod:)toTarget:selfwithObject:nil];
1.3:[objperformSelectorInBackground:@selector(threadMe)withObject:nil];
2、NSOperation:
NSOperation也是多线程的一种,NSOpertaion有2种形式.
(1)并发执行
并发执行你需要重载如下4个方法
//执行任务主函数,线程运行的入口函数
-(void)start
//是否允许并发,返回YES,允许并发,返回NO不允许。默认返回NO
-(BOOL)isConcurrent
-(BOOL)isExecuting
//是否已经完成,这个必须要重载,不然放在放在NSOperationQueue里的NSOpertaion不能正常释放。
-(BOOL)isFinished
比如TestNSOperation:NSOperaion重载上述的4个方法,
声明一个NSOperationQueue,NSOperationQueue*queue=[[[NSOperationQueuealloc]init]autorelease];
[queueaddOperation:testNSoperation];
它会自动调用TestNSOperation里的start函数,如果需要多个NSOperation,你需要设置queue的一些属性,如果多个NSOperation之间有依赖关系,也可以设置,具体可以参考API文档。
(2)非并发执行
-(void)main
只需要重载这个main方法就可以了。
3、GCD
GCD很强大,我的使用经验很少。但是scorpiozj总结的比较全面(http://www.cnblogs.com/scorpiozj/archive/2011/07/25/2116459.html)
同时,这篇文章也介绍的比较详细http://www.cnblogs.com/vinceoniphone/archive/2011/04/07/2007968.html
官方教程
GCD是和block紧密相连的,所以最好先了解下block(可以查看这里).GCD是Clevel的函数,这意味着它也提供了C的函数指针作为参数,方便了C程序员.
下面首先来看GCD的使用:
1
dispatch_async(dispatch_queue_tqueue,dispatch_block_tblock);
async表明异步运行,block代表的是你要做的事情,queue则是你把任务交给谁来处理了.(除了async,还有sync,delay,本文以async为例).
之所以程序中会用到多线程是因为程序往往会需要读取数据,然后更新UI.为了良好的用户体验,读取数据的操作会倾向于在后台运行,这样以避免阻塞主线程.GCD里就有三种queue来处理。
1.Mainqueue:
顾名思义,运行在主线程,由dispatch_get_main_queue获得.和ui相关的就要使用MainQueue.
2.Serialquque(privatedispatchqueue)
每次运行一个任务,可以添加多个,执行次序FIFO.通常是指程序员生成的,比如:
1
2
3
4
NSDate*da=[NSDatedate];
NSString*daStr=[dadescription];
constchar*queueName=[daStrUTF8String];
dispatch_queue_tmyQueue=dispatch_queue_create(queueName,NULL);
3.Concurrentqueue(globaldispatchqueue):
可以同时运行多个任务,每个任务的启动时间是按照加入queue的顺序,结束的顺序依赖各自的任务.使用dispatch_get_global_queue获得.
所以我们可以大致了解使用GCD的框架:
1
2
3
4
5
6
7
dispatch_async(getDataQueue,^{
//获取数据,获得一组后,刷新UI.
dispatch_aysnc(mainQueue,^{
//UI的更新需在主线程中进行
};
}
)
由此可见,GCD的使用非常简单,以我的使用经验来看,以后会逐步淘汰使用NSOperation而改用GCD。
提个问题:如果某个ViewController里运行了一个Thread,Thread还没结束的时候,这个ViewController被Release了,结果会如何?
经过的的测试,Thread不结束,ViewController一直保留,不会执行dealloc方法。
多线程开发之NSThread与NSOperation
利用iphone的NSThread多线程实现和线程同步
从接口的定义中可以知道,NSThread和大多数iphone的接口对象一样,有两种方式可以初始化:
一种使用initWithTargetid)targetselector:(SEL)selectorobject:(id)argument,但需要负责在对象的retaincount为0时调用对象的release方法清理对象。
另一种则使用所谓的convenientmethod,这个方便接口就是detachNewThreadSelector,这个方法可以直接生成一个线程并启动它,而且无需为线程的清理负责。
#import<UIKit/UIKit.h>
@interfaceSellTicketsAppDelegate:NSObject<UIApplicationDelegate>{
inttickets;
intcount;
NSThread*ticketsThreadone;
NSThread*ticketsThreadtwo;
NSCondition*ticketsCondition;
UIWindow*window;
}
@property(nonatomic,retain)IBOutletUIWindow*window;
@end
然后在实现中添加如下代码:
//SellTicketsAppDelegate.m
//SellTickets
//
//Createdbysundfsun2009on09-11-10.
//Copyright__MyCompanyName__2009.Allrightsreserved.
//
#import"SellTicketsAppDelegate.h"
@implementationSellTicketsAppDelegate
@synthesizewindow;
-(void)applicationDidFinishLaunching:(UIApplication*)application{
tickets=100;
count=0;
//锁对象
ticketCondition=[[NSConditionalloc]init];
ticketsThreadone=[[NSThreadalloc]initWithTarget:selfselector:@selector(run)object:nil];
[ticketsThreadonesetName:@"Thread-1"];
[ticketsThreadonestart];
ticketsThreadtwo=[[NSThreadalloc]initWithTarget:selfselector:@selector(run)object:nil];
[ticketsThreadtwosetName:@"Thread-2"];
[ticketsThreadtwostart];
//[NSThreaddetachNewThreadSelector:@selector(run)toTarget:selfwithObject:nil];
//Overridepointforcustomizationafterapplicationlaunch
[windowmakeKeyAndVisible];
}
-(void)run{
while(TRUE){
//上锁
[ticketsConditionlock];
if(tickets>0)
{
[NSThreadsleepForTimeInterval:0.5];
count=100-tickets;
NSLog(@"当前票数是:%d,售出:%d,线程名:%@",tickets,count,[[NSThreadcurrentThread]name]);
tickets--;
}else
{
break;
}
[ticketsConditionunlock];
}
}
-(void)dealloc{
[ticketsThreadonerelease];
[ticketsThreadtworelease];
[ticketsConditionrelease];
[windowrelease];
[superdealloc];
}
@end
-------------------------------------------------------------------------------------
//定义
#import<UIKit/UIKit.h>
@interfaceThreadSyncSampleViewController:UIViewController{
int_threadCount;
NSCondition*_myCondition;
}
@end
//实现文件如下:
#import"ThreadSyncSampleViewController.h"
@implementationThreadSyncSampleViewController
/*
//Thedesignatedinitializer.Overridetoperformsetupthatisrequiredbeforetheviewisloaded.
-(id)initWithNibName:(NSString*)nibNameOrNilbundle:(NSBundle*)nibBundleOrNil{
if(self=[superinitWithNibName:nibNameOrNilbundle:nibBundleOrNil]){
//Custominitialization
}
returnself;
}
*/
/*
//ImplementloadViewtocreateaviewhierarchyprogrammatically,withoutusinganib.
-(void)loadView{
}
*/
//ImplementviewDidLoadtodoadditionalsetupafterloadingtheview,typicallyfromanib.
-(void)viewDidLoad{
[superviewDidLoad];
//
//_myCondition=nil;
//
_myCondition=[[NSConditionalloc]init];
//
NSTimer*timer=[NSTimerscheduledTimerWithTimeInterval:30
target:self
selector:@selector(threadTester)
userInfo:nil
repeats:YES];
[timerfire];
}
-(void)threadTester{
[_myConditionlock];
_threadCount=-2;
//如果有n个要等待的thread,这里置成-n
[_myConditionunlock];
//
NSLog(@"");
NSLog(@"------------------------------------------------------------------------------");
[NSThreaddetachNewThreadSelector:@selector(threadOne)toTarget:selfwithObject:nil];
[NSThreaddetachNewThreadSelector:@selector(threadTwo)toTarget:selfwithObject:nil];
[NSThreaddetachNewThreadSelector:@selector(threadThree)toTarget:selfwithObject:nil];
return;
}
-(void)threadOne{
NSLog(@"@@@Inthread111111start.");
[_myConditionlock];
intn=rand()%5+1;
NSLog(@"@@@Thread111111Willsleep%dseconds,now_threadCountis:%d",n,_threadCount);
sleep(n);
//[NSThreadsleepForTimeInterval:n];
_threadCount++;
NSLog(@"@@@Thread111111hassleep%dseconds,now_threadCountis:%d",n,_threadCount);
[_myConditionsignal];
NSLog(@"@@@Thread1111111hassignaled,now_threadCountis:%d",_threadCount);
[_myConditionunlock];
NSLog(@"@@@Inthreadonecomplete.");
[NSThreadexit];
return;
}
-(void)threadTwo{
NSLog(@"###Inthread2222222start.");
[_myConditionlock];
intn=rand()%5+1;
NSLog(@"###Thread2222222Willsleep%dseconds,now_threadCountis:%d",n,_threadCount);
sleep(n);
//[NSThreadsleepForTimeInterval:n];
_threadCount++;
NSLog(@"###Thread2222222hassleep%dseconds,now_threadCountis:%d",n,_threadCount);
[_myConditionsignal];
NSLog(@"###Thread2222222hassignaled,now_threadCountis:%d",_threadCount);
[_myConditionunlock];
//_threadCount++;
NSLog(@"###Inthread2222222complete.");
[NSThreadexit];
return;
}
-(void)threadThree{
NSLog(@"<<<Inthread333333start.");
[_myConditionlock];
while(_threadCount<0){
[_myConditionwait];
}
NSLog(@"<<<Inthread333333,_threadCountnowis%d,willstartwork.",_threadCount);
[_myConditionunlock];
NSLog(@"<<<Inthread333333complete.");
[NSThreadexit];
return;
}
/*
//Overridetoalloworientationsotherthanthedefaultportraitorientation.
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
//ReturnYESforsupportedorientations
return(interfaceOrientation==UIInterfaceOrientationPortrait);
}
*/
-(void)didReceiveMemoryWarning{
//Releasestheviewifitdoesn'thaveasuperview.
[superdidReceiveMemoryWarning];
//Releaseanycacheddata,images,etcthataren'tinuse.
}
-(void)viewDidUnload{
//Releaseanyretainedsubviewsofthemainview.
//e.g.self.myOutlet=nil;
}
-(void)dealloc{
[_myConditionrelease];
[superdealloc];
}
多线程之NSInvocationOperation
多线程编程是防止主线程堵塞,增加运行效率等等的最佳方法。而原始的多线程方法存在很多的毛病,包括线程锁死等。在Cocoa中,Apple提供了NSOperation这个类,提供了一个优秀的多线程编程方法。
本次介绍NSOperation的子集,简易方法的NSInvocationOperation:
@implementationMyCustomClass
-(void)launchTaskWithData:(id)data{
//创建一个NSInvocationOperation对象,并初始化到方法
//在这里,selector参数后的值是你想在另外一个线程中运行的方法(函数,Method)
//在这里,object后的值是想传递给前面方法的数据
NSInvocationOperation*theOp=[[NSInvocationOperationalloc]initWithTarget:self
selector:@selector(myTaskMethod:)object:data];
//下面将我们建立的操作“Operation”加入到本地程序的共享队列中(加入后方法就会立刻被执行)
//更多的时候是由我们自己建立“操作”队列
[[MyAppDelegatesharedOperationQueue]addOperation:theOp];
}
//这个是真正运行在另外一个线程的“方法”
-(void)myTaskMethod:(id)data{
//Performthetask.
}
@end一个NSOperationQueue操作队列,就相当于一个线程管理器,而非一个线程。因为你可以设置这个线程管理器内可以并行运行的的线程数量等等。下面是建立并初始化一个操作队列:
@interfaceMyViewController:UIViewController{
NSOperationQueue*operationQueue;
//在头文件中声明该队列
}
@end
@implementationMyViewController
-(id)init{
self=[superinit];
if(self){
operationQueue=[[NSOperationQueuealloc]init];//初始化操作队列
[operationQueuesetMaxConcurrentOperationCount:1];
//在这里限定了该队列只同时运行一个线程
//这个队列已经可以使用了
}
returnself;
}
-(void)dealloc{
[operationQueuerelease];
//正如Alan经常说的,我们是程序的好公民,需要释放内存!
[superdealloc];
}
@end简单介绍之后,其实可以发现这种方法是非常简单的。很多的时候我们使用多线程仅仅是为了防止主线程堵塞,而NSInvocationOperation就是最简单的多线程编程,在iPhone编程中是经常被用到的。
///////////////////////////////////////////////////////////////////////////////////////////////////
1在主线程里加入一个loading画面……
2{
3[windowaddSubview:view_loading];
4[NSThreaddetachNewThreadSelector:@selector(init_backup:)toTarget:selfwithObject:nil];
5}
可以通过performSelectorOhMainThread更新UI元素,比如设置进度条等等。最后消除loading画面,载入主View。
7-(void)init_backup:(id)sender
8{
9NSAutoreleasePool*pool=[[NSAutoreleasePoolalloc]init];
10
11//...
12inti=status;
13[selfperformSelectorOnMainThread:@selector(show_loading:)withObject:[NSNumbernumberWithInt:i]waitUntilDone:NO];
14
15[view_loadingremoveFromSuperview];
16[windowaddSubview:tabcontroller_main.view];
17[poolrelease];
18}
///////////////////////////////////////////////////////
使用NSOperation和NSOperationQueue启动多线程
在appstore中的很多应用程序非常的笨重,他们有好的界面,但操作性很差,比如说当程序从网上或本地载入数据的时候,界面被冻结了,用户只能等程序完全载入数据之后才能进行操作。
当打开一个应用程序时,iphone会产生一个包含main方法的线程,所用程序中的界面都是运行在这个线程之中的(tableviews,tabbars,alerts…),有时候我们会用数据填充这些view,现在问题是如何有效的载入数据,并且用户还能自如的操作程序。方法是启动新的线程,专门用于数据的下载,而主线程不会因为下载数据被阻塞。
不管使用任何编程语言,在实现多线程时都是一件很麻烦的事情。更糟糕的是,一旦出错,这种错误通常相当糟糕。然而,幸运的是apple从osx10.5在这方面做了很多的改进,NSThread的引入,使得开发多线程应用程序容易多了。除此之外,它们还引入了两个全新的类,NSOperation和NSOperationQueue。
接下来我们通过一个实例来剖析如何使用这两个类实现多线程。这里指示展示这两个类的基本用法,当然这不是使用他们的唯一办法。
如果你熟悉java或者它的别的变种语言的话,你会发现NSOperation对象很像java.lang.Runnable接口,就像java.lang.Runnable接口那样,NSOperation类也被设计为可扩展的,而且只有一个需要重写的方法。它就是-(void)main。使用NSOperation的最简单的方式就是把一个NSOperation对象加入到NSOperationQueue队列中,一旦这个对象被加入到队列,队列就开始处理这个对象,直到这个对象的所有操作完成。然后它被队列释放。
下面的例子中,使用一个获取网页,并对其解析程NSXMLDocument,最后将解析得到的NSXMLDocument返回给主线程。
PageLoadOperation.h@interfacePageLoadOperation:NSOperation{
NSURL*targetURL;}
@property(retain)NSURL*targetURL;
-(id)initWithURL:(NSURL*)url;@end
PageLoadOperation.m
#import"PageLoadOperation.h"#import"AppDelegate.h"@implementationPageLoadOperation@synthesizetargetURL;-(id)initWithURL:(NSURL*)url;{
if(![superinit])returnnil;
[selfsetTargetURL:url];
returnself;}-(void)dealloc{
[targetURLrelease],targetURL=nil;
[superdealloc];
}
-(void)main
{
NSString*webpageString=[[[NSStringalloc]
initWithContentsOfURL:[selftargetURL]]autorelease];
NSError*error=nil;
NSXMLDocument*document=[[NSXMLDocumentalloc]
initWithXMLString:webpageString
opti*****:NSXMLDocumentTidyHTMLerror:&error];
if(!document){
NSLog(@"%sErrorloadingdocument(%@):%@",
_cmd,[[selftargetURL]absoluteString],error);
return;
}
[[AppDelegateshared]
performSelectorOnMainThread:@selector(pageLoaded:)
withObject:documentwaitUntilDone:YES];
[documentrelease];
}
@end
正如我们所看到的那样,这个类相当的简单,在它的init方法中接受一个url并保存起来,当main函数被调用的时候,它使用这个保存的url创建一个字符串,并将这个字符串传递给NSXMLDocumentinit方法。如果加载的xml数据没有出错,数据会被传递给AppDelegate,它处于主线程中。到此,这个线程的任务就完成了。在主线程中注销操作队列的时候,会将这个NSOperation对象释放。
AppDelegate.h
@interfaceAppDelegate:NSObject{
NSOperationQueue*queue;
}+(id)shared;-(void)pageLoaded:(NSXMLDocument*)document;@endAppDelegate.m#import"AppDelegate.h"#import"PageLoadOperation.h"@implementationAppDelegate
staticAppDelegate*shared;
staticNSArray*urlArray;
-(id)init
{
if(shared)
{
[selfautorelease];
returnshared;
}
if(![superinit])returnnil;NSMutableArray*array=[[NSMutableArrayalloc]init];[arrayaddObject:@"http://www.google.com"];[arrayaddObject:@"http://www.apple.com"];[arrayaddObject:@"http://www.yahoo.com"];[arrayaddObject:@"http://www.zarrastudios.com"];[arrayaddObject:@"http://www.macosxhints.com"];urlArray=array;queue=[[NSOperationQueuealloc]init];shared=self;returnself;
}
•(void)applicationDidFinishLaunching:
(NSNotification*)aNotification
{
for(NSString*urlStringinurlArray)
{
NSURL*url=
[NSURLURLWithString:urlString];PageLoadOperation*plo=
[[PageLoadOperationalloc]initWithURL:url];
[queueaddOperation:plo];
[plorelease];
}
}
-(void)dealloc
{
[queuerelease],queue=nil;
[superdealloc];
}
+(id)shared;
{
if(!shared){
[[AppDelegatealloc]init];
}
returnshared;
}
-(void)pageLoaded:(NSXMLDocument*)document;
{
NSLog(@"%sDosomethingwiththeXMLDocument:%@",
_cmd,document);
}
@end
NSOperationQueue的并行控制(NSOperationQueueConcurrency)
在上面这个简单的例子中,我们很难看出这些操作是并行运行的,然而,如果你你的操作花费的时间远远比这里的要长,你将会发现,队列是同时执行这些操作的。幸运的是,如果你想要为队列限制同时只能运行几个操作,你可以使用NSOperationQueue的setMaxConcurrentOperationCount:方法。例如,[queuesetMaxConcurrentOperationCount:2];