iOS多執行緒NSOperation篇

Perfect_Dream發表於2017-12-19

The NSOperation  class is an abstract class you use to encapsulate the code and data associated with a single task. Because it is abstract, you do not use this class directly but instead subclass or use one of the system-defined subclasses (NSInvocation​Operation or NSBlock​Operation) to perform the actual task. Despite being abstract, the base implementation of NSOperation does include significant logic to coordinate the safe execution of your task. The presence of this built-in logic allows you to focus on the actual implementation of your task, rather than on the glue code needed to ensure it works correctly >with other system objects.   -- 引自官方文件NSOperation Class Reference

官方文件很清晰的給我們解釋了NSOperation是個什麼玩意,所以這裡也就不贅述NSOperation的基礎定義了,接下來會對NSOperation.h為我們提供的一些屬性和API做一些簡單的釋義,順便看一下怎麼使用。

NSOperation.h

NSOperation.h裡邊共包含四個類是屬性與API,先來看一下NSOperation的:

1. NSOperation

NSOperation的屬性與方法:

@interface NSOperation : NSObject {
@private
    id _private;
    int32_t _private1;
#if __LP64__
    int32_t _private1b;
#endif
}

- (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<NSOperation *> *dependencies;

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
	NSOperationQueuePriorityVeryLow = -8L,
	NSOperationQueuePriorityLow = -4L,
	NSOperationQueuePriorityNormal = 0,
	NSOperationQueuePriorityHigh = 4,
	NSOperationQueuePriorityVeryHigh = 8
};

@property NSOperationQueuePriority queuePriority;

@property (nullable, 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 (nullable, copy) NSString *name NS_AVAILABLE(10_10, 8_0);

@end
複製程式碼

以上,就是NSOperation.h對我們提供所有屬性和方法。接下來就慢慢的看看它們的使用吧。

  • -(void)start;

    從名字就可以看出來它的作用,開始執行某個NSOperation。除此之外還有另外兩個需要我們知道的知識:

    1. 放入佇列中的NSOperation物件不需要呼叫start方法,NSOPerationQueue會在『合適』的時機去自動呼叫。當然,如果你不需要NSOperationQueue去呼叫也可以手動呼叫,這塊知識後邊會講到
    2. start方法的重寫問題,這個問題也留到後邊繼承模組講
    3. 手動的呼叫start方法後,這個操作會在當前呼叫的執行緒進行同步執行,所以在主執行緒裡面自己一定要小心的呼叫,不然就會把主執行緒給卡死。
  • -(void) main;

    這個方法在純粹的NSOperation的使用中基本用不到,特殊情況就不考慮了,在後邊繼承模組會講到關於main方法重寫問題需要注意的事情。

  • -(void) cancel;

    關於cancel方法官方文件是這樣解釋的Advises the operation object that it should stop executing its task.也就是說呼叫這個方法之後執行緒並不會立即停止,只是會做一個標誌,系統會在之後找合適的時機停止這個執行緒。

  • @property (readonly, getter=isCancelled) BOOL cancelled;

    這個屬性也是證明了cancel方法呼叫之後並不會立即停止operation操作,cancelled這個屬性就是查詢操作過的operation物件是否新增取消標誌。

  • @property (readonly, getter=isExecuting) BOOL executing;

  • @property (readonly, getter=isFinished) BOOL finished;

  • @property (readonly, getter=isConcurrent) BOOL concurrent; // To bedeprecated; use and override 'asynchronous' below

  • @property (readonly, getter=isAsynchronous) BOOL asynchronous NS_AVAILABLE(10_8, 7_0);

  • @property (readonly, getter=isReady) BOOL ready;

    接下來這幾個屬性都是用來表示operation操作的狀態的,這邊會涉及到一個知識:operation的狀態,從operation建立開始到結束會有多個狀態,上邊的屬性就是為我們提供查詢operation操作的狀態進入到哪一步了,可以針對狀態作對響應的操作,狀態列表會在NSOperation模組最後做給出。

  • (void)addDependency:(NSOperation )op;

  • (void)removeDependency:(NSOperation )op;

    這兩個方法是設定依賴的,關於依賴的釋義會在本模組後邊給出。

  • @property (readonly, copy) NSArray<NSOperation *>*dependencies;

    官方文件是這樣解釋這個屬性的An array of the operation objects that must finish executing before the current object can begin executing.也就是說,這個屬性是查詢某個operation執行之前需要執行的所有操作。

  • @property NSOperationQueuePriority queuePriority;

    設定operation操作的優先順序,在它之前有這樣一個屬性:thread​Priority ,不過在iOS8.0之後已經不使用了,下邊是operation的優先順序列舉:

    typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
    };
    複製程式碼
  • @property (nullable, copy) void (^completionBlock)(void) NS_AVAILABLE(10_6, 4_0);

    The block to execute after the operation’s main task is completed.,簡單來說就是當所有的operation操作結束以後想做什麼操作可以在這個block裡邊寫。

  • - (void)waitUntilFinished NS_AVAILABLE(10_6, 4_0);

    停止當前任務,知道操作物件將其他任務全部執行完畢之後再執行。

  • @property double threadPriority NS_DEPRECATED(10_6, 10_10, 4_0, 8_0);

    同queuePriority。

  • @property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);

    這是iOS8.0以後蘋果提供的新功能,通過這個屬性告訴系統我們在進行什麼樣的工作,然後系統會通過合理的資源控制來最高效的執行任務程式碼,其中主要涉及到CPU排程的優先順序、IO優先順序、任務執行在哪個執行緒以及執行的順序等等,我們通過一個抽象的Quality of Service引數來表明任務的意圖以及類別。

    NSQualityOfServiceUserInteractive
    與使用者互動的任務,這些任務通常跟UI級別的重新整理相關,比如動畫,這些任務需要在一瞬間完成
    NSQualityOfServiceUserInitiated
    由使用者發起的並且需要立即得到結果的任務,比如滑動scroll view時去載入資料用於後續cell的顯示,這些任務通常跟後續的使用者互動相關,在幾秒或者更短的時間內完成
    NSQualityOfServiceUtility
    一些可能需要花點時間的任務,這些任務不需要馬上返回結果,比如下載的任務,這些任務可能花費幾秒或者幾分鐘的時間
    NSQualityOfServiceBackground
    這些任務對使用者不可見,比如後臺進行備份的操作,這些任務可能需要較長的時間,幾分鐘甚至幾個小時
    NSQualityOfServiceDefault
    優先順序介於user-initiated 和 utility,當沒有     QoS資訊時預設使用,開發者不應該使用這個值來設定自己的任務
    複製程式碼
  • @property (nullable, copy) NSString *name NS_AVAILABLE(10_10, 8_0);

    operation物件的名稱

以上,就是NSOperation.h為我們提供的所有關於NSOperation的屬性與方法。

狀態

  • is​Cancelled - read-only
  • is​Asynchronous - read-only
  • is​Executing - read-only
  • is​Finished - read-only
  • is​Ready - read-only
  • dependencies - read-only
  • queue​Priority - readable and writable
  • completion​Block - readable and writable

很多時候我們都需要根據當前操作的狀態做後續的事情,比如在完成下載任務後更新主執行緒。以上幾種狀態都是基於keypath的KVO通知決定,所以在你手動改變某個操作的狀態時,請別忘了手動傳送通知。這裡面每個屬性都是相互獨立的,同時只可能有一個狀態是YES。如果你手動管理過某個操作的狀態,切記在操作完成後將finished這個狀態設定為YES,因為在NSOperationQueue所管理的佇列中,只有isFinished為YES時才將其移除佇列,如果不修改這個值,很可能會產生記憶體或者死鎖問題。

依賴

關於依賴的說明最好看一下官方文件,下邊這段話是官方文件裡邊對依賴的介紹:

Dependencies are a convenient way to execute operations in a specific order. You can add and remove dependencies for an operation using the add​Dependency:​ and remove​Dependency:​ methods. By default, an operation object that has dependencies is not considered ready until all of its dependent operation objects have finished executing. Once the last dependent operation finishes, however, the operation object becomes ready and able to execute.

簡單來說就是確定任務之間的關係,設定任務執行的順序。新增和刪除依賴的方法上邊已經說過了。需要注意的是依賴不能互相設定,比如A依賴B,B又依賴於A,這樣就會導致死鎖!還有一點必須要注意的是,在每個操作完成時,請將isFinished設定為YES,不然後續的操作是不會開始執行的。

2. NSBlockOperation

首先要說NSBlockOperation是繼承自NSOperation的,在其上做了一些封裝。NSBlockOperation的官方網址在這:NSBlockOperation。 需要知道的關於NSBlockOperation的知識:

  1. 預設的執行方式是並行的,並且有最大併發數量,但是機型不同最大併發數量也不相同,所以我們不需要關係最大併發數量的問題。
  2. 新增到block裡邊的事件並不會完全開闢子執行緒執行,而是優先放到主執行緒執行。
  3. 因為是整合於NSOperation,所以NSOperation的一些特性都具有,比如:
    • 支援一個可選的 completion block ,這個 block 將會在 operation 的主任務執行完成時被呼叫;
    • 支援通過 KVO 來觀察 operation 執行狀態的變化;
    • 支援設定執行的優先順序,從而影響 operation 之間的相對執行順序;
    • 支援取消操作,可以允許我們停止正在執行的 operation 。

NSBlockOperation在NSOperation的基礎上為我們新提供了一個類方法,一個例項方法和一個屬性:

  • + (instancetype)blockOperationWithBlock:(void (^)(void))block;

    伴隨著一個block建立一個operation操作

  • - (void)addExecutionBlock:(void (^)(void))block;

    新增一個block,block裡邊就是需要執行的任務程式碼

  • *@property (readonly, copy) NSArray<void (^)(void)> executionBlocks;

    存放所有新增到當前operation的操作

下邊是使用方式:

NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"第一個執行緒");
}];
[blockOperation addExecutionBlock:^{
    NSLog(@"第二個執行緒");
}];
[blockOperation addExecutionBlock:^{
    NSLog(@"第三個執行緒");
}];
[blockOperation addExecutionBlock:^{
    NSLog(@"第四個執行緒");
}];

NSLog(@"%@",blockOperation.executionBlocks);

[blockOperation start];
複製程式碼

3. NSInvocationOperation

NSInvocationOperation也是整合自NSOperation的,如果是單獨使用的話並沒有什麼太大的作用,需要跟著後邊NSOperationQueue配合使用才會產生效果。這裡是官方文件地址:NSInvocationOperation

下邊是屬性和API:

  • - (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;

    初始化一個NSInvocationOperation例項,後邊和button新增點選事件比較像,傳入一個目標,一個事件,最後一個是引數。

  • - (instancetype)initWithInvocation:(NSInvocation *)inv NS_DESIGNATED_INITIALIZER;

    根據傳入NSInvocation建立一個NSInvocationOperation例項。

  • @property (readonly, retain) NSInvocation *invocation;

    列印NSInvocationOperation的invocation屬性,只讀。

  • @property (nullable, readonly, retain) id result;

    這個屬性是標誌著傳入事件的返回結果

需要注意的是:如果建立完畢後直接呼叫start方法,那麼這個操作預設會在主執行緒中執行。

4. NSOperationQueue

接下來就是NSOperationQueue了,這個類在iOS的日常開發中還是比較常見的。首先還是NSOperationQueue的官方地址:NSOperationQueue。 接下來就一邊看著官方文件一邊寫釋義吧

The NSOperation​Queue  class regulates the execution of a set of NSOperation  objects. After being added to a queue, an operation remains in that queue until it is explicitly canceled or finishes executing its task. Operations within the queue (but not yet executing) are themselves organized according to priority levels and inter-operation object dependencies and are executed accordingly. An application may create multiple operation queues and submit operations to any of them.

這是官方文件裡對NSOperationQueue的介紹。將建立好的Operation物件新增到NSOperationQueue操作佇列裡,新增之後操作將保留在該佇列中,直到手動取消或此任務被執行完畢才會被移除釋放。佇列內的操作(尚未執行)根據優先順序和互操作物件依賴性進行組織,並相應執行。應用程式可以建立多個操作佇列並向其中任何一個提交操作。

然後來看一下NSOperation​Queue屬性與方法:

@interface NSOperationQueue : NSObject {
@private
    id _private;
    void *_reserved;
}

- (void)addOperation:(NSOperation *)op;
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait NS_AVAILABLE(10_6, 4_0);

- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);

@property (readonly, copy) NSArray<__kindof NSOperation *> *operations;
@property (readonly) NSUInteger operationCount NS_AVAILABLE(10_6, 4_0);

@property NSInteger maxConcurrentOperationCount;

@property (getter=isSuspended) BOOL suspended;

@property (nullable, copy) NSString *name NS_AVAILABLE(10_6, 4_0);

@property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);

@property (nullable, assign /* actually retain */) dispatch_queue_t underlyingQueue NS_AVAILABLE(10_10, 8_0);

- (void)cancelAllOperations;

- (void)waitUntilAllOperationsAreFinished;

#if FOUNDATION_SWIFT_SDK_EPOCH_AT_LEAST(8)
@property (class, readonly, strong, nullable) NSOperationQueue *currentQueue NS_AVAILABLE(10_6, 4_0);
@property (class, readonly, strong) NSOperationQueue *mainQueue NS_AVAILABLE(10_6, 4_0);
#endif

@end
複製程式碼

上邊還是Operation.h檔案裡邊為我們提供的NSOperationQueue的屬性和方法,然後接下來就是詳細的解釋:

  • - (void)addOperation:(NSOperation *)op;

    給佇列裡邊新增一個Operation操作。

  • - (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait NS_AVAILABLE(10_6, 4_0);

    給佇列裡邊新增一組Operation操作,並且決定是否阻塞當前執行緒,知道所有Operation操作執行完畢。

  • - (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);

    利用block建立一個新的Operation操作,並且新增到佇列裡邊。

  • @property (readonly, copy) NSArray<__kindof NSOperation *> *operations;

    用來儲存佇列裡邊所有的Operation操作。

  • @property (readonly) NSUInteger operationCount NS_AVAILABLE(10_6, 4_0);

    任務數量

  • @property NSInteger maxConcurrentOperationCount;

    最大併發數量,預設為-1,就是不限制併發數量。設定為 1 的話就相當於FIFO。如需自定義,作為移動端開發,一般以 3 以內為宜,因為雖然任務是在子執行緒進行處理的,但是 CPU 處理這些過多的子執行緒可能會影響 UI,讓 UI 變卡。

  • @property (getter=isSuspended) BOOL suspended;

    佇列是否正常執行,如果為NO的話就會阻塞佇列的執行。可以用來做暫停和繼續。

  • @property (nullable, copy) NSString *name NS_AVAILABLE(10_6, 4_0);

    佇列名稱

  • @property NSQualityOfService qualityOfService NS_AVAILABLE(10_10, 8_0);

    和Operation的qualityOfService一樣,這裡就不多做解釋了

  • @property (nullable, assign * actually retain *) dispatch_queue_t underlyingQueue NS_AVAILABLE(10_10, 8_0);

    iOS8.0以後出來的屬性,並沒有試出來是幹嘛的,也沒感覺有什麼作用,官方文件是這樣說明的

    The dispatch queue used to execute operations. The default value of this property is nil. You can set the value of this property to an existing dispatch queue to have enqueued operations interspersed with blocks submitted to that dispatch queue. The value of this property should only be set if there are no operations in the queue; setting the value of this property when operation​Count is not equal to 0 raises an NSInvalid​Argument​Exception. The value of this property must not be the value returned by dispatch_get_main_queue. The quality-of-service level set for the underlying dispatch queue overrides any value set for the operation queue's quality​Of​Service property.

以後找到用到的地方再回來補吧

  • - (void)cancelAllOperations;

    取消佇列裡邊所有的Operation操作

  • - (void)waitUntilAllOperationsAreFinished;

    阻塞當前任務的執行,直到佇列裡邊其他任務全部執行完畢之後再繼續執行。

  • *@property (class, readonly, strong, nullable) NSOperationQueue currentQueue NS_AVAILABLE(10_6, 4_0);

    返回當前操作所在的操作佇列

  • @property (class, readonly, strong) NSOperationQueue *mainQueue NS_AVAILABLE(10_6, 4_0);

    返回與主執行緒關聯的操作佇列

以上,就是NSOperationQueue的屬性與方法。

附上一段基礎方法與屬性的使用:

NSOperationQueue*queue = [[NSOperationQueue alloc]init];
NSOperation*op1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"執行任務1");
    sleep(2);
}];
NSOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"執行任務4");
    sleep(2);
}];
NSOperation*op2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"執行任務2,並且取消任務4");
    [op4 cancel];
    sleep(2);
}];

NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"執行任務3");
    sleep(2);
    //回主執行緒渲染圖片
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        NSLog(@"回主執行緒執行任務");
        sleep(2);
    }];
}];

NSOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"執行任務5");
    sleep(2);
}];

NSOperation *op6 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"執行任務6");
    sleep(2);
}];

NSOperation *op7 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"執行任務7");
    sleep(2);
}];

//監聽任務是否執行完畢
op1.completionBlock = ^(){
    NSLog(@"任務1執行完畢");
    sleep(2);
};
op2.completionBlock = ^(){
    NSLog(@"任務2執行完畢");
    sleep(2);
};
op3.completionBlock = ^(){
    NSLog(@"任務3執行完畢");
    sleep(2);
};
op4.completionBlock = ^(){
    NSLog(@"任務4執行完畢");
    sleep(2);
};
op5.completionBlock = ^(){
    NSLog(@"任務5執行完畢");
    sleep(2);
};
op6.completionBlock = ^(){
    NSLog(@"任務6執行完畢");
    sleep(2);
};
op7.completionBlock = ^(){
    NSLog(@"任務7執行完畢");
    sleep(2);
};
//新增依賴
[op2 addDependency:op1];
[op3 addDependency:op2];
[op4 addDependency:op3];
[op5 addDependency:op3];
[op6 addDependency:op5];
[op7 addDependency:op6];
//新增任務
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
[queue addOperation:op6];
[queue addOperation:op7];

[queue waitUntilAllOperationsAreFinished];
NSLog(@"所有任務都執行完畢了");
複製程式碼

執行一下這段程式碼會發現幾個問題:

  1. op5不同於其他操作,他是直接依賴於op3的。
  2. op4並沒有列印執行程式碼,但是執行完畢程式碼被列印了。
  3. 並不是任務執行完畢之後才會執行下一個任務,就算是有依賴關係也是這樣
  4. 任務3裡邊的返回主執行緒任務是在所有任務執行完畢之後才執行的

上述問題就不做解釋了,大家可以自己考慮一下為什麼。

OperationQueue和DispatchQueue的不同:

  • 不遵循 FIFO(先進先出):在 Operation Queues 中,你可以設定 operation(操作)的執行優先順序,並且可以在 operation 之間新增依賴,這意味著你可以定義某些 operation,使得它們可以在另外一些 operation 執行完畢之後再被執行。這就是為什麼它們不遵循先進先出的順序。
  • 預設情況下 Operation Queues 是併發執行:雖然你不能將其改成序列佇列,但還是有一種方法,通過在 operation 之間新增相依性來讓 Operation Queues 中的任務按序執行。
  • Operation Queues 是 NSOperationQueue 類的例項,任務被封裝在 NSOperation 的例項中。

自定義NSOperation

當上述內容都不能滿足你的需要時,我們可以嘗試自己定義一個NSOperation。 如果你的應用只需要非併發的NSOperation,自定義的時候必須要重寫的方法只有一個:

  • main方法
 - (void)main
{
    NSLog(@"main begin");
    //捕獲異常
    @try {
        //在這裡我們要建立自己的釋放池,因為這裡我們拿不到主執行緒的釋放池
        @autoreleasepool {
            // 提供一個變數標識,來表示需要執行的操作是否完成了,當然,沒開始執行之前,為NO
            BOOL taskIsFinished = NO;
            // while 保證:只有當沒有執行完成和沒有被取消,才執行自定義的相應操作
            while (taskIsFinished == NO && [self isCancelled] == NO){
                // 自定義的操作
                sleep(10);  // 睡眠模擬耗時操作
                NSLog(@"currentThread = %@", [NSThread currentThread]);
                NSLog(@"mainThread    = %@", [NSThread mainThread]);
                // 這裡相應的操作都已經完成,後面就是要通知KVO我們的操作完成了。
                taskIsFinished = YES;
            }
        }
    }
    @catch (NSException *exception) {
        NSLog(@"Exception %@", exception);
    }
    NSLog(@"main end");
}
複製程式碼

如果我們的應用需要併發的NSOperation,自定義的時候需要做的事情就比較多了,首先看一下這個表格:

| 方法 | 描述 | | :: |::| | start | (必選)所有的併發Operation必需重寫這個方法並且要實現這個方法的內容來代替原來的操和。手動執行一個操作,你可以呼叫start方法。因此,這個方法的實現是這個操作的始點,也是其他執行緒或者執行這你這個任務的起點。注意一下,在這裡永遠不要呼叫[super start]。 | | main | (可選)這個方法就是你的實現的操作(懶得翻譯了,哈哈) | | isExecuting 和 isFinish | (必選)併發佇列負責維持當前操作的環境和告訴外部呼叫者當前的執行狀態。因此,一個併發佇列必需維持保持一些狀態資訊以至於知道什麼時候執行任務,什麼時候完成任務。它必須通過這些方法告訴外部當前的狀態。這種而且這些方法必須是執行緒安全,當狀態發生改變的時候,你必須使用KVO通知監聽這些狀態的物件 | | isConcurrent | (必選)定義一個併發操作,重寫這個方法並且返回YES |

所以說,如果我們想要自定義一個併發的NSOperation,我們可能需要重寫的方法如下:

  • start: 所有並行的 Operations 都必須重寫這個方法,然後在你想要執行的執行緒中手動呼叫這個方法。注意:任何時候都不能呼叫父類的start方法。
  • main: 在start方法中呼叫,但是注意要定義獨立的自動釋放池與別的執行緒區分開。
  • isExecuting: 是否執行中,需要實現KVO通知機制。
  • isFinished: 是否已完成,需要實現KVO通知機制。
  • isConcurrent: 該方法現在已經由isAsynchronous方法代替,並且NSOperationQueue 也已經忽略這個方法的值。
  • isAsynchronous: 該方法預設返回 NO ,表示非併發執行。併發執行需要自定義並且返回 YES。後面會根據這個返回值來決定是否併發。

與非併發操作不同的是,需要另外自定義一個方法來執行操作而不是直接呼叫-(void)start;方法

下邊是一份示例程式碼

- (id)init {  
    if(self = [super init])  
    {  
        executing = NO;  
        finished = NO;  
    }  
    return self;  
}  
- (BOOL)isConcurrent {  
    return YES;  
}  
- (BOOL)isExecuting {  
    return executing;  
}  
- (BOOL)isFinished {  
    return finished;  
}  
  
- (void)start {  
      
    //第一步就要檢測是否被取消了,如果取消了,要實現相應的KVO  
    if ([self isCancelled]) {  
        [self willChangeValueForKey:@"isFinished"];  
        finished = YES;  
        [self didChangeValueForKey:@"isFinished"];  
        return;  
    }  
      
    //如果沒被取消,開始執行任務  
    [self willChangeValueForKey:@"isExecuting"];  
      
    [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];  
    executing = YES;  
    [self didChangeValueForKey:@"isExecuting"];  
}  
  
- (void)main {  
// 異常捕獲
    @try {  
      // 防止記憶體不釋放
        @autoreleasepool {  
            //在這裡定義自己的併發任務  
            NSLog(@"自定義併發操作NSOperation");  
            NSThread *thread = [NSThread currentThread];  
            NSLog(@"%@",thread);  
              
            //任務執行完成後要實現相應的KVO  
            [self willChangeValueForKey:@"isFinished"];  
            [self willChangeValueForKey:@"isExecuting"];  
              
            executing = NO;  
            finished = YES;  
              
            [self didChangeValueForKey:@"isExecuting"];  
            [self didChangeValueForKey:@"isFinished"];  
        }  
    }  
    @catch (NSException *exception) {    
    }  
}  
複製程式碼

OK,以上就是NSOperation的初步調研結果,後續有時間會針對它的實現原理續寫一篇文章。



有志者、事竟成,破釜沉舟,百二秦關終屬楚;

苦心人、天不負,臥薪嚐膽,三千越甲可吞吳.

相關文章