iOS 多线程之NSOperation篇举例详解
这篇博客是接着总篇iOS GCD NSOperation NSThread等多线程各种举例详解写的一个支篇。总篇也包含了此文的链接。本文讲解的知识点有NSBlockOperationClick,队列,队列中如何加Operation,Operation中如何加任务,Operation之间的串行、并行,监控任务完成时机及其他一些关于NSOperation的方法,每个知识点都有例子和详细分析。附上demo下载地址
一、NSOperation介绍
NSOperation 是苹果公司对 GCD 的封装,完全面向对象。NSOperation实例封装了需要执行的操作和执行操作所需的数据,并且能够以并发或非并发的方式执行这个操作。NSOperation本身是抽象基类,因此可以使用它的子类NSInvocationOperation 和 NSBlockOperation,或者自定义子类也行。NSOperation 和 NSOperationQueue 可以看成是GCD的任务和队列。
二、NSInvocationOperation(不是类型安全的,苹果在swift里不用它了)
但是我还是来讲讲,证明它曾经存在过。NSInvocationOperation创建一个 Operation 后,需要调用 start 方法来启动任务,它会 默认在当前队列同步执行。举个例子
- (IBAction)NSInvocationOperationClick:(id)sender { //1.创建NSInvocationOperation对象 NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(myTask) object:nil]; operation.completionBlock = ^() { NSLog(@"执行完毕"); }; //2.在当前线程执行 [operation start]; NSLog(@"阻塞我没有?当前线程%@",[NSThread currentThread]); } //模拟很耗时的任务 -(void)myTask { for (NSInteger i = 0; i < 500000000; i++) { if (i == 0) { NSLog(@"任务 -> 开始"); } if (i == 499999999) { NSLog(@"任务 -> 完成"); } } }
打印结果:
分析结论:从打印结果看,会阻塞当前线程,因为他是同步执行的。
三、NSBlockOperationClick
在block中加任务,使用更方便,代码更紧凑,举个例子。
- (IBAction)NSBlockOperationClick:(id)sender { //1.创建NSBlockOperation对象 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ [self myTask]; }]; //2.也可以添加多个Block,通过这个方法可以给 Operation 添加多个执行 Block。这样 Operation 中的任务 会并发执行,它会 在主线程和其它的多个线程 执行这些任务 for (NSInteger n = 0; n < 3; n++) { [operation addExecutionBlock:^{ for (NSInteger i = 0; i < 500000000; i++) { if (i == 0) { NSLog(@"任务%ld -> 开始",n); } if (i == 499999999) { NSLog(@"任务%ld -> 完成",n); } } }]; } operation.completionBlock = ^() { NSLog(@"执行完毕"); }; //3.开始任务 [operation start]; NSLog(@"阻塞我没有?当前线程%@",[NSThread currentThread]); }
打印结果:
分析结论:给 Operation 添加多个执行 Block任务,Operation 中的任务会并发执行,它会在主线程和其它的多个线程执行这些任务,会阻塞当前主线程。
四、主队列
举个例子
//主队列里的任务并行执行,且不阻塞当前线程。(队列中加多个operation时另说,看第五个例子就知道了) - (IBAction)mainQueue:(id)sender { NSOperationQueue *queue = [NSOperationQueue mainQueue]; //创建NSBlockOperation对象 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ [self myTask]; }]; for (NSInteger n = 0; n < 3; n++) { [operation addExecutionBlock:^{ for (NSInteger i = 0; i < 500000000; i++) { if (i == 0) { NSLog(@"主队列中任务%ld -> 开始%@",n,[NSThread currentThread]); } if (i == 499999999) { NSLog(@"主队列中任务%ld -> 完成",n); } } }]; } operation.completionBlock = ^() { NSLog(@"执行完毕"); }; //加入到队列中任务自动执行 [queue addOperation:operation]; NSLog(@"阻塞我没有?当前线程%@",[NSThread currentThread]); }
打印结果:
分析结论:主队列里的任务都是另开线程并行执行的,不会阻塞当前线程。(队列中加多个operation时另说,请看下面例子)
五、其他队列
基本用法跟主队列差不多,我将在这个例子里列举更多用法。在一个队列里加2个operation,第一个operation里加2个任务。举个例子
- (IBAction)otherQueue:(id)sender { //1.创建一个其他队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; //最大并发数,用来设置最多可以让多少个operation同时执行。当你把它设置为 1 的时候,就是串行了(指多个operation的串行,同一个operation中的任务是并行的) queue.maxConcurrentOperationCount = 1; //2.创建NSBlockOperation对象 NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{ for (NSInteger i = 0; i < 500000000; i++) { if (i == 0) { NSLog(@"operation1中任务1 -> 开始"); } if (i == 499999999) { NSLog(@"operation1中任务1 -> 完成"); } } }]; //3.给operation1再加一个任务 [operation1 addExecutionBlock:^{ for (NSInteger i = 0; i < 500000000; i++) { if (i == 0) { NSLog(@"operation1中任务2 -> 开始"); } if (i == 499999999) { NSLog(@"operation1中任务2 -> 完成"); } } }]; operation1.completionBlock = ^() { NSLog(@"执行完毕"); }; //4.再加一个operation2 NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ for (NSInteger i = 0; i < 500000000; i++) { if (i == 0) { NSLog(@"operation2中任务 -> 开始"); } if (i == 499999999) { NSLog(@"operation2中任务 -> 完成"); } } }]; //5.加入到队列中任务自动执行,waitUntilFinished为yes会阻塞当前线程,为no不阻塞 [queue addOperations:@[operation1,operation2] waitUntilFinished:NO]; NSLog(@"阻塞我没有?当前线程%@",[NSThread currentThread]); }
打印结果:
分析结论:2个operation是串行的,但同一个operation中的多个任务是并行的
六、任务依赖
任务一完成的情况下才能执行任务二,任务二完成的情况下才能执行任务三,举个例子。
//任务依赖 - (IBAction)addDependency:(id)sender { //1.任务一 NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"任务1开始"); [NSThread sleepForTimeInterval:1.0]; NSLog(@"任务1完成"); }]; //2.任务二 NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"任务2开始"); [NSThread sleepForTimeInterval:1.0]; NSLog(@"任务2完成"); }]; //3.任务三 NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"任务3开始"); [NSThread sleepForTimeInterval:1.0]; NSLog(@"任务3完成"); }]; //4.设置依赖 [operation2 addDependency:operation1]; //任务二依赖任务一 [operation3 addDependency:operation2]; //任务三依赖任务二 //5.创建队列并加入任务 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO]; }
打印结果:
分析结论:该任务依赖的任务完成了,才能执行该任务。
七、其他相关方法
// 暂停queue [queue setSuspended:YES]; // 继续queue [queue setSuspended:NO]; //会阻塞当前线程,等到某个operation执行完毕 [operation waitUntilFinished]; // 阻塞当前线程,等待queue的所有操作执行完毕 [queue waitUntilAllOperationsAreFinished]; // 取消单个操作 [operation cancel]; // 取消queue中所有的操作 [queue cancelAllOperations];