這篇博客是接着總篇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];