本文詳細介紹了在實現同步、異步 NSOperation 時分別須要實現哪些方法、注意哪些問題。最後對 GCD 與 NSOperation Queue 做了一個簡單的對比。html
本文同時發表於個人我的博客ios
在 iOS 中實現併發編程主要有三種方式:GCD、NSOperation Queue以及Thread,其中前二者使用普遍。 在正式開始以前有必要區分兩組概念:同步、異步與串行、並行。git
dispatch_sync
),異步方法則是將任務拋出去,在任務沒完成前就返回(如:dispatch_async
);NSOperation Queue + NSOperation 做爲 iOS 中『高級的、面向對象的併發編程方式』耳熟能詳,但具體到一些細節問題上認識每每又比較模糊。本文在蘋果官方文檔 Concurrency Programming Guide、NSOperation Class Reference 以及 NSOperationQueue Class Reference 的基礎上作了一次疏理和總結。github
NSOperation
自己是個抽象類,在使用前必須子類化(系統預約義了兩個子類:NSInvocationOperation
和 NSBlockOperation
)。那問題來了,在子類化過程當中,須要重寫父類的哪些方法?編程
這首先就要了解一下NSOperation
類中幾個重要方法的默認實現: 安全
因爲操做 NSOperation 與 NSOperation 任務的執行每每在不一樣的線程上進行,在繼續以前須要強調線程安全問題:『NSOperation 自己是 thread-safe,當咱們在子類重寫或自定義方法時一樣須要保證 thread-safe』。網絡
對於 Synchronous Operation,在調用其 start
方法的線程上同步執行該 operation 的任務,start
方法返回時 operation 執行完成。所以,對於 Synchronous Operation 通常只需重寫 main
方法便可(start
方法的默認實現已實現相關 KVO 功能)。併發
然而對於 Asynchronous Operation,調用其 start
方法後,在 start
返回時 operation 的任務可能還沒完成(爲了實現異步,通常須要在其餘線程執行 operation 的具體任務)。所以 start
方法默認實現不能知足異步須要(默認實現會在start
返回前將 isExecuting
置爲 NO、isFinished
置爲 YES,併產生 KVO 通知)。此時至少須要重寫如下方法:app
start
方法來實現,能夠經過建立子線程或其餘異步方式完成。同時須要在任務開始前將 isExecuting
置爲YES 並拋出 KVO 通知。 『重寫的 start
方法必定不能調用 [super start]
』isExecuting
上拋出 KVO 通知isFinished
上拋出 KVO 通知這裏咱們看看著名的網絡框架 AFNetworking 中關於 NSOperation 的使用:框架
AFNetworking 3.0 全面使用
NSURLSession
,而NSURLSession
自己是異步的、且沒有NSURLConnection
須要 runloop 配合的問題,所以在3.0版本中並無使用 NSOperation,代碼獲得很大的簡化。這裏咱們說的是 AFNetworking 2.3.1 版本。
在 AFNetworking 中 AFURLConnectionOperation 是個異步的 NSOperation 子類,其 start
方法以下:
start
方法的實現能夠看到:
NSURLConnection
的網絡回調必需要有 runloop 的配合,經過port-based input source 喚醒 runloop 處理網絡事件),也就是說 AFURLConnectionOperation 是在一條常駐子線程中處理網絡回調。前面咱們提到 operation 被 cancel 時也被認爲是完成,這點在自定義 start
時一樣須要注意:
cancelConnection
以及
connection:didFailWithError:
方法中都會調用其
finish
方法:
cancel
方法後該如何處理徹底由咱們自定義的
start
方法決定(固然良好的設計應該要符合 cancel 的語義)。
同時,AFURLConnectionOperation 也實現瞭如下方法:
dependencies: 咱們能夠在 operation 間添加依賴關係,在某個 operation 所依賴的 operations 完成以前,其一直處於未就緒狀態(isReady
爲 NO)。 須要注意的是,依賴關係是 operation 自身的狀態,也就是說有依賴關係的 operations 能夠處在不一樣的 NSOperationQueue 中。
isReady: isReady
默認實現主要處理 operation 間的依賴關係,當咱們自定義該方法時須要考慮 super
的值,如 AFURLConnectionOperation中關於 isReady
的實現:
qualityOfService: 用於表示 operation 在獲取系統資源時的優先級,默認值:NSQualityOfServiceBackground
,咱們能夠根據須要給 operation 賦不一樣的優化級,如最高優化級:NSQualityOfServiceUserInteractive
。
queuePriority: 用於設置 operation 在 operation queue 中的相對優化級,同一 queue 中優化級高的 operation(isReady
爲 YES) 會被優先執行。須要注意區分qualityOfService
(在系統層面,operation 與其餘線程獲取資源的優先級)與queuePriority
(同一 queue 中 operation 間執行的優化級)的區別。 同時,須要注意dependencies
(嚴格控制執行順序)與queuePriority
(queue 內部相對優先級)的區別。
NSOperation Queue 用於管理、執行 NSOperation,不管其中的 operation 是並行仍是串行,queue 都會在子線程(借用 GCD)中執行 operation。 從上小節咱們知道,實現異步 operation 比同步 operation 要複雜許多,所以若是打算將 operation 加入 queue 中,則徹底能夠將 operation 實現爲同步方式。 對於 queue 中已就緒的 operation,queue 會選擇 queuePriority
值最大的 operation 執行。
關於 NSOperation Queue 有兩點須要強調:
cancel
方法。(從上小節咱們知道,對 operation 調用 cancel
方法後的效果徹底由 operation 本身決定。cancel
惟一能影響的就是清除 operation 的依賴關係,使其當即能夠被執行)。此時 queue 並不會 remove 其中的 operations,remove 操做僅發生在 operation 完成時。GCD 與 NSOperation Queue 做爲常見的併發編程方式,在使用時該如何選擇? 首先,對比一下咱們關心的幾個問題:
別忘了,咱們還有第三種選擇:NSThread。因爲使用 NSThread 時須要處理線程相關的問題,通常不多使用。但不管是 GCD 仍是 NSOperation Queue,其中的任務具體什麼時候執行是由系統控制的,對於實時性要求很高的任務則可使用 NSThread。
本文簡單討論了在使用 NSOperation 時須要重寫哪些方法、注意哪些問題。同時也對 GCD 與 NSOperation Queue 做了簡單對比,在清楚了它們各自的特色以後再作選擇時會更加清晰。