多線程之NSOperation簡介

在iOS開發中,爲了提高用戶體驗,咱們一般會將操做耗時的操做放在主線程以外的線程進行處理。對於正常的簡單操做,咱們更多的是選擇代碼更少的GCD,讓咱們專一於本身的業務邏輯開發。NSOperation在ios4後也基於GCD實現,可是相對於GCD來講可控性更強,而且能夠加入操做依賴。ios

NSOperation是一個抽象的基類,表示一個獨立的計算單元,能夠爲子類提供有用且線程安全的創建狀態,優先級,依賴和取消等操做。系統已經給咱們封裝了NSBlockOperation和NSInvocationOperation這兩個實體類。使用起來也很是簡單,不過咱們更多的使用是本身繼承並定製本身的操做。安全

NSOperation定義

- (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,則後面操做均可以不用在執行了。函數

completionBlock

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"];
}
相關文章
相關標籤/搜索