概述:
1.NSThread 一般用做调试用,需要程序员管理生命周期,开发中较少使用。
2.GCD(iOS 4.0) 由系统管理,开发中使用的很多。
3.NSOperation(iOS 2.0) 基于GCD的OC封装,开发中使用的较多。
核心概念:同步/异步,全局队列/主队列
全局队列: { 同步:不开 异步:开N条 } 主队列(奇葩): { 同步:卡死,不要用 异步:不开,因为他有主线程 } /** 开不开线程线程由任务是同步还是异步 同步:打死都不开 异步:除了主队列,都开,开多少条,由我们的队列的类型来决定 */
使用方式1:异步下载图片,回到主线程更新UI
//一般下载,可能会下载多个 dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"downLoad----%@",[NSThread currentThread]); //去做耗时间的操作 //下载图像...最后得到一个UIImage //去主线程更新UI dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"updateUI----%@",[NSThread currentThread]); }); });
使用方式2:线程安全-设置依赖关系(例如你异步下载20张图片写入一个数组中,为了保证效率所以数组为非线程安全的,如果解决多线程同时访问的问题)
解决办法是间接通过GCD的阻塞(dispatch_barrier_async)和同步
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //创建一个并发的队列 /** 注意:如果你用了dispatch_barrier_async必须要用我们自己创建的并发队列,而不能用全局队列 */ _concurrentQueue = dispatch_queue_create("com.yun", DISPATCH_QUEUE_CONCURRENT); //循环去请求20个图片资源 for (int i=0; i<20; ++i) { [self loadPic:i]; } } - (void)loadPic:(int)i{ //模拟网络,通过URL去访问Bundle里面的图片 dispatch_async(_concurrentQueue, ^{ [NSThread sleepForTimeInterval:1.0]; NSString *fileName = [NSString stringWithFormat:@"%02d.jpg",i%10+1]; //1.URL NSURL *url =[[NSBundle mainBundle] URLForResource:fileName withExtension:nil]; //2.去Bundle里面加载我们的图片二进制数据 NSData *data =[NSData dataWithContentsOfURL:url]; //3.将图片的二进制数据,转成UIImage对象 UIImage *image = [UIImage imageWithData:data]; //4.将我们的图像添加到photoList数组中去 NSLog(@"%@----%d",[NSThread currentThread],i); dispatch_barrier_async(_concurrentQueue, ^{ [self.photoList addObject:image]; }); }); }
常用方式3:单例(dispatch_once)的创建和延迟执行(dispatch_after)
常见用法一:添加异步任务之间的依赖
/** 1.登录 2.付费 3.下载 4.通知用户 1.依赖的代码,必须放在添加任务前 2.不要造成循环依赖,iOS7之前是直接崩,iOS8,不调度了 */ - (void)depencyDemo{ NSLog(@"%s",__FUNCTION__); NSBlockOperation *login = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@----login",[NSThread currentThread]); }]; NSBlockOperation *notifyUser = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@----notifyUser",[NSThread currentThread]); }]; NSBlockOperation *downLoad = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@----downLoad",[NSThread currentThread]); }]; NSBlockOperation *pay = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@----pay",[NSThread currentThread]); }]; NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; //GCD,同步,串行队列 //NSOperation,addDependency就是表示我这个任务,要依赖于哪个任务执行完 [downLoad addDependency:pay]; [pay addDependency:login]; [notifyUser addDependency:downLoad]; //[login addDependency:downLoad];//??? //将任务添加到`队列`中去 // [mainQueue addOperation:login]; // [mainQueue addOperation:notifyUser]; // [self.concurrentQueue addOperation:downLoad]; // [mainQueue addOperation:pay]; // 注意download是放在异步线程里的 [self.concurrentQueue addOperation:downLoad]; //YES,同步, 主队列千万不要和同步搞在一起 [mainQueue addOperations:@[login,notifyUser,pay] waitUntilFinished:NO]; }
打印结果如下:
[ViewController depencyDemo] <NSThread: 0x7fa88a707320>{number = 1, name = main}----login <NSThread: 0x7fa88a707320>{number = 1, name = main}----pay <NSThread: 0x7fa88a4bf850>{number = 2, name = (null)}----downLoad <NSThread: 0x7fa88a707320>{number = 1, name = main}----notifyUser
用法二:支持KVO可以观察操作状态(正在执行、是否结束、是否取消)
用法三:支持设置最大并发数(省电)而GCD不可以
用法四:异步耗时操作主队列更新UI
//1.创建一个并发队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; //2.创建任务,并将其添加到`并发队列中` [queue addOperationWithBlock:^{ NSLog(@"login===>%@",[NSThread currentThread]); [NSThread sleepForTimeInterval:2.0]; //去主线程更新UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [NSThread sleepForTimeInterval:3.0]; //更新UI的代码 NSLog(@"%@===>",[NSThread currentThread]); }]; }];
有新的发现会继续来完善....
iOS多线程NSThread/GCD/NSOperation区别和使用
原文:http://www.cnblogs.com/zhaoyunboy/p/how-to-use-gcd-nsoperation.html