在iOS開發中,爲了提高用戶體驗,咱們一般會將操做耗時的操做放在主線程以外的線程進行處理。對於正常的簡單操做,咱們更多的是選擇代碼更少的GCD,讓咱們專一於本身的業務邏輯開發。NSOperation在ios4後也基於GCD實現,可是相對於GCD來講可控性更強,而且能夠加入操做依賴。ios
NSOperation是一個抽象的基類,表示一個獨立的計算單元,能夠爲子類提供有用且線程安全的創建狀態,優先級,依賴和取消等操做。系統已經給咱們封裝了NSBlockOperation和NSInvocationOperation這兩個實體類。使用起來也很是簡單,不過咱們更多的使用是本身繼承並定製本身的操做。安全
- (void)start; - (void)main; @property (readonly, getter=isCancelled) BOOL cancelled; - (void)cancel; @property (readonly, getter=isExecuting) BOOL executing; @property (readonly, getter=isFinished) BOOL finished; @property (readonly, getter=isConcurrent) BOOL concurrent; // To be deprecated; use and override 'asynchronous' below @property (readonly, getter=isAsynchronous) BOOL asynchronous NS_AVAILABLE(10_8, 7_0); @property (readonly, getter=isReady) BOOL ready; - (void)addDependency:(NSOperation *)op; - (void)removeDependency:(NSOperation *)op; @property (readonly, copy) NSArray *dependencies; typedef NS_ENUM(NSInteger, NSOperationQueuePriority) { NSOperationQueuePriorityVeryLow = -8L, NSOperationQueuePriorityLow = -4L, NSOperationQueuePriorityNormal = 0, NSOperationQueuePriorityHigh = 4, NSOperationQueuePriorityVeryHigh = 8 }; @property NSOperationQueuePriority queuePriority; @property (copy) void (^completionBlock)(void) NS_AVAILABLE(10_6, 4_0); - (void)waitUntilFinished NS_AVAILABLE(10_6, 4_0); @property double threadPriority NS_DEPRECATED(10_6, 10_10, 4_0, 8_0); @property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0); @property (copy) NSString *name NS_AVAILABLE(10_10, 8_0);
NSOperation提供了ready
cancelled
executing
finished
這幾個狀態變化,咱們的開發也是必須處理本身關心的其中的狀態。這些狀態都是基於keypath的KVO通知決定,因此在你手動改變本身關心的狀態時,請別忘了手動發送通知。這裏面每一個屬性都是相互獨立的,同時只可能有一個狀態是YES。finished
這個狀態在操做完成後請及時設置爲YES,由於NSOperationQueue所管理的隊列中,只有isFinished爲YES時纔將其移除隊列,這點在內存管理和避免死鎖很關鍵。網絡
NSOperation中咱們能夠爲操做分解爲若干個小的任務,經過添加他們之間的依賴關係進行操做,這點在設計上是頗有意義的。好比咱們最經常使用的圖片異步加載,第一步咱們是去經過網絡進行加載,第二步咱們可能須要對圖片進行下處理(調整大小或者壓縮保存)。咱們能夠直接調用- (void)addDependency:(NSOperation*)op;
這個方法添加依賴:多線程
[imgRsizingOperation addDependency:networkOperation]; [operationQueue addOperation:networkOperation]; [operationQueue addOperation:imgRsizingOperation];
這點咱們必需要注意的是不能添加相互依賴,像A依賴B,B依賴A,這樣會致使死鎖!還有一點必需要注意的時候,在每一個操做完成時,請將isFinished
設置爲YES,否則後續的操做是不會開始執行的。異步
執行一個operation有兩種方法,第一種是本身手動的調用start
這個方法,這種方法調用會在當前調用的線程進行同步執行,因此在主線程裏面本身必定要當心的調用,否則就會把主線程給卡死,還不如直接用GCD呢。第二種是將operation添加到operationQueue中去,這個也是咱們用得最多的也是提倡的方法。NSOperationQueue會在咱們添加進去operation的時候儘快進行執行。固然若是NSOperationQueue的maxConcurrentOperationCount
若是設置爲1的話,進至關於FIFO了。async
隊列是怎麼調用咱們的執行的操做的呢?若是你只是想弄一個同步的方法,那很簡單,你只要重寫main
這個函數,在裏面添加你要的操做。若是想定義異步的方法的話就重寫start
方法。在你添加進operationQueue中的時候系統將自動調用你這個start方法,這時將再也不調用main裏面的方法。ide
NSOperation容許咱們調用-(void)cancel
取消一個操做的執行。固然,這個操做並非咱們所想象的取消。這個取消的步驟是這樣的,若是這個操做在隊列中沒有執行,那麼這個時候取消並將狀態finished
設置爲YES,那麼這個時候的取消就是直接取消了。若是這個操做已經在執行了,那麼咱們只能等其操做完成。當咱們調用cancel方法的時候,他只是將isCancelled
設置爲YES。因此,在咱們的操做中,咱們應該在每一個操做開始前,或者在每一個有意義的實際操做完成後,先檢查下這個屬性是否是已經設置爲YES。若是是YES,則後面操做均可以不用在執行了。函數
iOS4後添加了這個block,在這個操做完成時,將會調用這個block一次,這樣也很是方便的讓咱們對view進行更新或者添加本身的業務邏輯代碼。atom
operationQueue有maxConcurrentOperationCount
設置,當隊列中operation不少時而你想讓後續的操做提早被執行的時候,你能夠爲你的operation設置優先級線程
NSOperationQueuePriorityVeryLow = -8L, NSOperationQueuePriorityLow = -4L, NSOperationQueuePriorityNormal = 0, NSOperationQueuePriorityHigh = 4, NSOperationQueuePriorityVeryHigh = 8
最後咱們看看一個簡單的小示例,在.m文件裏面咱們將重寫finished
executing
兩個屬性。咱們重寫set方法,手動發送keyPath的KVO通知。在start
函數中,咱們首先判斷是否已經取消,若是取消的話,咱們將直接return,並將finished
設置爲YES。若是沒有取消操做,咱們將_executing
設置爲YES,表示當前operation正在執行,繼續執行咱們的邏輯代碼。在執行完咱們的代碼後,別忘了設置operation的狀態,將_executing
設置爲NO,並將finished
設置爲YES,這樣咱們就已經很簡單的完成了咱們的多線程操做任務。
@interface TestOperation () @property (nonatomic, assign) BOOL finished; @property (nonatomic, assign) BOOL executing; @end @implementation TestOperation @synthesize finished = _finished; @synthesize executing = _executing; - (void)start { if ([self isCancelled]) { _finished = YES; return; } else { _executing = YES; //start your task; //end your task _executing = NO; _finished = YES; } } - (void)setFinished:(BOOL)finished { [self willChangeValueForKey:@"isFinished"]; _finished = finished; [self didChangeValueForKey:@"isFinished"]; } - (void)setExecuting:(BOOL)executing { [self willChangeValueForKey:@"isExecuting"]; _executing = executing; [self didChangeValueForKey:@"isExecuting"]; }