一、簡介
一個NSOperation對象可以通過調用start方法來執行任務,默認是同步執行的。也可以將NSOperation添加到一個NSOperationQueue(操作隊列)中去執行,而且是異步執行的。
創建一個操作隊列:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
二、添加NSOperation到NSOperationQueue中
1.添加一個operation
[queue addOperation:operation];
2.添加一組operation
[queue addOperations:operations waitUntilFinished:NO];
3.添加一個block形式的operation
[queue addOperationWithBlock:^() {
NSLog(@"執行一個新的操作,線程:%@", [NSThread currentThread]);
}];
NSOperation添加到queue之后,通常短時間內就會得到運行。但是如果存在依賴,或者整個queue被暫停等原因,也可能需要等待。
注意:NSOperation添加到queue之后,絕對不要再修改NSOperation對象的狀態。因為NSOperation對象可能會在任何時候運行,因此改變NSOperation對象的依賴或數據會產生不利的影響。你只能查看NSOperation對象的狀態, 比如是否正在運行、等待運行、已經完成等
三、添加NSOperation的依賴對象1.當某個NSOperation對象依賴于其它NSOperation對象的完成時,就可以通過addDependency方法添加一個或者多個依賴的對象,只有所有依賴的對象都已經完成操作,當前NSOperation對象才會開始執行操作。另外,通過removeDependency方法來刪除依賴對象。
[operation2 addDependency:operation1];
依賴關系不局限于相同queue中的NSOperation對象,NSOperation對象會管理自己的依賴, 因此完全可以在不同的queue之間的NSOperation對象創建依賴關系
唯一的限制是不能創建環形依賴,比如A依賴B,B依賴A,這是錯誤的
2.依賴關系會影響到NSOperation對象在queue中的執行順序,看下面的例子:
1> 沒有設置依賴關系
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"執行第1次操作,線程:%@", [NSThread currentThread]);
}]; NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"執行第2次操作,線程:%@", [NSThread currentThread]);
}];
[queue addOperation:operation1];
[queue addOperation:operation2];
打印信息:
2013-02-03 00:21:35.024 thread[5616:3d13] 執行第1次操作,線程:<NSThread: 0x7658570>
{name = (null), num = 3}
2013-02-03 00:21:35.063 thread[5616:1303] 執行第2次操作,線程:<NSThread: 0x765a2e0>
{name = (null), num = 4}
可以看出,默認是按照添加順序執行的,先執行operation1,再執行operation2
2> 設置了依賴關系
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"執行第1次操作,線程:%@", [NSThread currentThread]);
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"執行第2次操作,線程:%@", [NSThread currentThread]);
}];
// operation1依賴于operation2
[operation1 addDependency:operation2];
[queue addOperation:operation1];
[queue addOperation:operation2];
打印信息:
2013-02-03 00:24:16.260 thread[5656:1b03] 執行第2次操作,線程:<NSThread: 0x7634490>
{name = (null), num = 3}
2013-02-03 00:24:16.285 thread[5656:1303] 執行第1次操作,線程:<NSThread: 0x9138b50>
{name = (null), num = 4}
可以看出,先執行operation2,再執行operation1
四、修改Operations的執行順序
對于添加到queue中的operations,它們的執行順序取決于2點:
1.首先看看NSOperation是否已經準備好:是否準備好由對象的依賴關系確定
2.然后再根據所有NSOperation的相對優先級來確定。優先級等級則是operation對象本身的一個屬性。默認所有operation都擁有“普通”優先級,不過可以通過setQueuePriority:方法來提升或降低operation對象的優先級。優先級只能應用于相同queue中的operations。如果應用有多個operation queue,每個queue的優先級等級是互相獨立的。因此不同queue中的低優先級操作仍然可能比高優先級操作更早執行。
注意:優先級不能替代依賴關系,優先級只是對已經準備好的 operations確定執行順序。先滿足依賴關系,然后再根據優先級從所有準備好的操作中選擇優先級最高的那個執行。
五、設置隊列的最大并發操作數量
隊列的最大并發操作數量,意思是隊列中最多同時運行幾條線程
雖然NSOperationQueue類設計用于并發執行Operations,你也可以強制單個queue一次只能執行一個Operation。setMaxConcurrentOperationCount:方法可以配置queue的最大并發操作數量。設為1就表示queue每次只能執行一個操作。不過operation執行的順序仍然依賴于其它因素,比如operation是否準備好和operation的優先級等。因此串行化的operation queue并不等同于GCD中的串行dispatch queue
// 每次只能執行一個操作
queue.maxConcurrentOperationCount = 1;
// 或者這樣寫
[queue setMaxConcurrentOperationCount:1];
六、取消Operations
一旦添加到operation queue,queue就擁有了這個Operation對象并且不能被刪除,唯一能做的事情是取消。你可以調用Operation對象的cancel方法取消單個操作,也可以調用operation queue的cancelAllOperations方法取消當前queue中的所有操作。
// 取消單個操作
[operation cancel];
// 取消queue中所有的操作
[queue cancelAllOperations];
七、等待Options完成
為了最佳的性能,你應該設計你的應用盡可能地異步操作,讓應用在Operation正在執行時可以去處理其它事情。如果需要在當前線程中處理operation完成后的結果,可以使用NSOperation的waitUntilFinished方法阻塞當前線程,等待operation完成。通常我們應該避免編寫這樣的代碼,阻塞當前線程可能是一種簡便的解決方案,但是它引入了更多的串行代碼,限制了整個應用的并發性,同時也降低了用戶體驗。絕對不要在應用主線程中等待一個Operation,只能在第二或次要線程中等待。阻塞主線程將導致應用無法響應用戶事件,應用也將表現為無響應。
// 會阻塞當前線程,等到某個operation執行完畢
[operation waitUntilFinished];
除了等待單個Operation完成,你也可以同時等待一個queue中的所有操作,使用NSOperationQueue的waitUntilAllOperationsAreFinished方法。注意:在等待一個 queue時,應用的其它線程仍然可以往queue中添加Operation,因此可能會加長線程的等待時間。
// 阻塞當前線程,等待queue的所有操作執行完畢
[queue waitUntilAllOperationsAreFinished];
八、暫停和繼續queue
如果你想臨時暫停Operations的執行,可以使用queue的setSuspended:方法暫停queue。不過暫停一個queue不會導致正在執行的operation在任務中途暫停,只是簡單地阻止調度新Operation執行。你可以在響應用戶請求時,暫停一個queue來暫停等待中的任務。稍后根據用戶的請求,可以再次調用setSuspended:方法繼續queue中operation的執行
// 暫停queue
[queue setSuspended:YES];
// 繼續queue
[queue setSuspended:NO];