iOS中多线程编程主要分为NSThread、NSOperation和GCD,今天主要记录下自己在学习NSOperation中的点滴~如有不对的地方帮忙指出下,PS:人生第一次写blog,各位看官请轻虐,谢啦~
NSOperation是abstract类,不能直接使用,可以使用CocoTouch提供的NSBlockOperaion和NSInvocationOperation,也可以自己实现subclass。NSOperation可以理解为一个独立的任务,没有调度功能,真正利用NSOperation实现多线程的关键是NSOperationQueue,当NSOperation 添加到NSOperationQueue后,NSOperationQueue就会给队列中的NSOperation分配线程并调度。
1、NSOperationQueue为队列中的NSOperation分配不同的线程,测试代码如下:
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 // Do any additional setup after loading the view, typically from a nib. 4 NSLog(@"main Thread is %p",[NSThread mainThread]); 5 [self testOperationWithQueue]; 6 } 7 - (void)testOperationWithQueue 8 { 9 NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; 10 NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{ 11 [NSThread sleepForTimeInterval:1]; 12 NSLog(@"blockOperation1:current Thread is %p",[NSThread currentThread]); 13 }]; 14 NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{ 15 [NSThread sleepForTimeInterval:1]; 16 NSLog(@"blockOperation2:current Thread is %p",[NSThread currentThread]); 17 }]; 18 NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{ 19 [NSThread sleepForTimeInterval:1]; 20 NSLog(@"blockOperation3:current Thread is %p",[NSThread currentThread]); 21 }]; 22 [operationQueue addOperation:blockOperation1]; 23 [operationQueue addOperation:blockOperation2]; 24 [operationQueue addOperation:blockOperation3]; 25 } 26 27 打印结果: 28 2015-05-29 23:34:22.747 TestNSOperation[12730:1171127] main Thread is 0x7fc279f28130 29 2015-05-29 23:34:31.288 TestNSOperation[12730:1171237] blockOperation1:current Thread is 0x7fc279e0b8a0 30 2015-05-29 23:34:31.288 TestNSOperation[12730:1171238] blockOperation3:current Thread is 0x7fc279d661c0 31 2015-05-29 23:34:31.288 TestNSOperation[12730:1171235] blockOperation2:current Thread is 0x7fc279e064c0
从打印结果可以看出blockOperation1、blockOperation2、blockOperation3运行于不同的线程,是同时运行的,不需要等其他NSOperation,这也表明NSOperationQueue默认是并发执行。如果想要serial执行,则可以设置operationQueue.maxConcurrentOperationCount = 1;
2、不用NSOperationQueue,也可以直接调用NSOperation的start方法,但此时NSOepration运行在当前线程上,测试代码如下:
1 - (void)testOperationWithQueue 2 { 3 NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; 4 NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{ 5 [NSThread sleepForTimeInterval:1]; 6 NSLog(@"blockOperation1:current Thread is %p",[NSThread currentThread]); 7 }]; 8 NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{ 9 [NSThread sleepForTimeInterval:1]; 10 NSLog(@"blockOperation2:current Thread is %p",[NSThread currentThread]); 11 }]; 12 NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{ 13 [NSThread sleepForTimeInterval:1]; 14 NSLog(@"blockOperation3:current Thread is %p",[NSThread currentThread]); 15 }]; 16 [operationQueue addOperation:blockOperation1]; 17 [operationQueue addOperation:blockOperation2]; 18 [operationQueue addOperation:blockOperation3]; 19 } 20 打印结果: 21 2015-05-30 00:22:20.497 TestNSOperation[12968:1196358] main Thread is 0x7fb562427fd0 22 2015-05-30 00:22:20.498 TestNSOperation[12968:1196358] blockOperation1:current Thread is 0x7fb562427fd0 23 2015-05-30 00:22:20.499 TestNSOperation[12968:1196358] blockOperation2:current Thread is 0x7fb562427fd0 24 2015-05-30 00:22:20.499 TestNSOperation[12968:1196358] blockOperation3:current Thread is 0x7fb562427fd0
从打印结果可以看出blockOperation都运行在mainThread上。
3、NSOperation还提供了cancel功能,cancel不是强制把你的代码stop掉,只是改变了NSOperation内部的状态,该功能只能cancel掉Ready、Finish状态的NSOperation,不能cancel掉正在executing的NSOperation,测试代码如下:
1 - (void)testCancelOperation 2 { 3 NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; 4 NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{ 5 NSLog(@"start blockOperation1!"); 6 [NSThread sleepForTimeInterval:10]; 7 if(blockOperation1.isCancelled) 8 { 9 NSLog(@"blockOperation2 cancelled!"); 10 return; 11 } 12 NSLog(@"blockOperation1:current Thread is %p",[NSThread currentThread]); 13 }]; 14 NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{ 15 NSLog(@"blockOperation2:current Thread is %p",[NSThread currentThread]); 16 }]; 17 NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{ 18 NSLog(@"blockOperation3:current Thread is %p",[NSThread currentThread]); 19 }]; 20 operationQueue.maxConcurrentOperationCount = 1; 21 [operationQueue addOperation:blockOperation1]; 22 [operationQueue addOperation:blockOperation2]; 23 [operationQueue addOperation:blockOperation3]; 24 [NSThread sleepForTimeInterval:2]; 25 NSLog(@"cancel blockOperation1"); 26 [blockOperation1 cancel]; 27 NSLog(@"cancel blockOperation2"); 28 [blockOperation2 cancel]; 29 NSLog(@"cancel blockOperation3"); 30 [blockOperation3 cancel]; 31 } 32 打印结果: 33 2015-05-30 00:27:26.311 TestNSOperation[13009:1199727] main Thread is 0x7ff02ae24e70 34 2015-05-30 00:27:26.312 TestNSOperation[13009:1199831] start blockOperation1! 35 2015-05-30 00:27:28.318 TestNSOperation[13009:1199727] cancel blockOperation1 36 2015-05-30 00:27:28.318 TestNSOperation[13009:1199727] cancel blockOperation2 37 2015-05-30 00:27:28.318 TestNSOperation[13009:1199727] cancel blockOperation3 38 2015-05-30 00:27:36.315 TestNSOperation[13009:1199831] blockOperation1:current Thread is 0x7ff02af1dec0
从打印结果可以看出不能cancel掉blockOperation1,而blockOperation2和blockOperation3则被成功地取消了。
4、NSOpertion还可以设置依赖,这一功能解决了需要按一定次序执行的situation,不多说直接上代码:
1 - (void)testOperationWithDependency 2 { 3 NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; 4 NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{ 5 NSLog(@"start blockOperation1"); 6 }]; 7 NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{ 8 [NSThread sleepForTimeInterval:2]; 9 NSLog(@"start blockOperation2"); 10 }]; 11 NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{ 12 [NSThread sleepForTimeInterval:3]; 13 NSLog(@"start blockOperation3");; 14 }]; 15 [blockOperation1 addDependency:blockOperation2]; 16 [blockOperation2 addDependency:blockOperation3]; 17 [operationQueue addOperation:blockOperation1]; 18 [operationQueue addOperation:blockOperation2]; 19 [operationQueue addOperation:blockOperation3]; 20 } 21 打印结果: 22 2015-05-31 18:18:06.649 TestNSOperation[15541:1510356] main Thread is 0x7fdaf9712a00 23 2015-05-31 18:18:09.654 TestNSOperation[15541:1510384] start blockOperation3 24 2015-05-31 18:18:11.660 TestNSOperation[15541:1510384] start blockOperation2 25 2015-05-31 18:18:11.660 TestNSOperation[15541:1510384] start blockOperation1
从打印的结果可以看出只有当依赖的Oepration执行完之后才开始执行自己。
5、当NSOperation添加到NSOperationQueue中,即使进入background(超过10min)NSOperation也会执行,真的不可思议啊,不是app进入到后台后如果不开定位服务、循环播放无声音乐、VOIP就最多运行10min吗?怎么超过10min的NSOperation还能执行啊,麻烦知道的网友告知一下,万分感谢~测试代码如下:
1 - (void)testBackgroundOperation 2 { 3 NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; 4 NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{ 5 [NSThread sleepForTimeInterval:11*60]; 6 NSLog(@"blockOperation1:current Thread is %p",[NSThread currentThread]); 7 }]; 8 [operationQueue addOperation:blockOperation1]; 9 } 10 打印结果: 11 2015-05-30 01:04:40.511 TestNSOperation[13252:1219020] main Thread is 0x7faf41c0da30 12 2015-05-30 01:04:43.095 TestNSOperation[13252:1219020] applicationDidEnterBackground! 13 2015-05-30 01:15:40.526 TestNSOperation[13252:1219146] blockOperation1:current Thread is 0x7faf41e02f90
从打印结果可以看出,进入后台11min后,blockOperation1执行了,这是为什么呀!!!!
6、疑问
1、NSOperationQueue具体如何管理NSOperation的,网上也没找到相关文章,希望知道的朋友给个链接;2、测试中发现当NSOperationQueue是局部变量,只要NSOperation添加到NSOperationQueue中,即使程序运行到超出NSOperationQueue的生命周期外(NSOperationQueue变量自动释放了),NSOperation依然可以执行,比如如下代码,operationQueue应该是释放了,但blockOperation1、blockOperation2依然运行,其实这2个疑问都是关于NSOperationQueue如何管理NSOperation的,再次恳求知道的网友给个链接,谢谢哈~
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 // Do any additional setup after loading the view, typically from a nib. 4 NSLog(@"main Thread is %p",[NSThread mainThread]); 5 [self testOperationLeaveLifeCycle]; 6 NSLog(@"leave operationQueue lifecycle"); 7 } 8 - (void)testOperationLeaveLifeCycle 9 { 10 NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; 11 NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{ 12 [NSThread sleepForTimeInterval:10]; 13 NSLog(@"blockOperation1:current Thread is %p",[NSThread currentThread]); 14 }]; 15 NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{ 16 [NSThread sleepForTimeInterval:20]; 17 NSLog(@"blockOperation2:current Thread is %p",[NSThread currentThread]); 18 }]; 19 [operationQueue addOperation:blockOperation1]; 20 [operationQueue addOperation:blockOperation2]; 21 } 22 打印结果: 23 2015-05-30 01:25:49.649 TestNSOperation[13358:1228380] main Thread is 0x7ff7e27241b0 24 2015-05-30 01:25:49.650 TestNSOperation[13358:1228380] leave operationQueue lifecycle 25 2015-05-30 01:25:59.652 TestNSOperation[13358:1228482] blockOperation1:current Thread is 0x7ff7e2517d20 26 2015-05-30 01:26:09.653 TestNSOperation[13358:1228481] blockOperation2:current Thread is 0x7ff7e2608a50
备注:上述所有的测试代码放到github上了,共勉~https://github.com/iOSGeek0829/testNSOperation