玩轉iOS開發:iOS中的NSOperation開發(二)

CainLuo發表於2017-08-15

文章分享至我的個人技術部落格: https://cainluo.github.io/15019907895337.html


在上一章, 我們把NSOperation的一些基礎概念和一些簡單的用法給摸了一遍, 但在示例當中, 我們發現了只單獨使用NSOperation的話, 只會在主執行緒裡執行, 雖然NSBlockOperation額外可以新增任務在子執行緒裡執行, 但這還是不夠滴, 這次我們接下來就把NSOperationNSOperationQueue配合使用給補全.

如果沒有看過上一篇文章的話, 可以去看看玩轉iOS開發:iOS中的NSOperation開發(一).

轉載宣告:如需要轉載該文章, 請聯絡作者, 並且註明出處, 以及不能擅自修改本文.


NSOperationQueue

NSOperationQueueGCD的佇列有些區別, 在NSOperationQueue裡, 分為主佇列和其他佇列, 而GCD是有並行佇列, 序列佇列.

NSOperationQueue中的其他佇列, 具有並行和序列功能, 那麼我們就來看看它們是怎麼耍的:

建立主佇列:

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
複製程式碼
  • 凡是新增到主佇列執行的任務, 都會放到主執行緒去執行.

建立其他佇列:

NSOperationQueue *otherQueue = [[NSOperationQueue alloc] init];
複製程式碼
  • 新增到其他佇列裡的任務, 就會自動放到子執行緒去執行, 這裡包括了並行和序列兩種方式.

新增任務到NSOperationQueue中

前面我們也提到了, NSOperation需要配合著NSOperationQueue來實現多執行緒操作, 那麼這裡就有兩種實現的方式.

第一種方式

使用下面這個方法來將任務新增到佇列中:

- (void)addOperation:(NSOperation *)op;
複製程式碼
- (void)addMissionToOperationQueue {
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self
                                                                                      selector:@selector(runInvocationOperation)
                                                                                        object:nil];
    
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        
        for (NSInteger i = 0; i < 2; i++) {
            
            NSLog(@"blockOperation執行第%zd任務, 當前的執行緒為: %@", i, [NSThread currentThread]);
        }
    }];
    
    [queue addOperation:invocationOperation];
    [queue addOperation:blockOperation];
}

- (void)runInvocationOperation {
    
    for (NSInteger i = 0; i < 3; i++) {
        
        NSLog(@"invocationOperation執行第%zd任務, 當前的執行緒為: %@", i, [NSThread currentThread]);
    }
}
複製程式碼
2017-08-06 12:06:53.950 NSOperationQueue-Example[2325:157835] invocationOperation執行第0任務, 當前的執行緒為: <NSThread: 0x60000006c540>{number = 3, name = (null)}
2017-08-06 12:06:53.950 NSOperationQueue-Example[2325:157825] blockOperation執行第0任務, 當前的執行緒為: <NSThread: 0x608000074180>{number = 4, name = (null)}
2017-08-06 12:06:53.955 NSOperationQueue-Example[2325:157835] invocationOperation執行第1任務, 當前的執行緒為: <NSThread: 0x60000006c540>{number = 3, name = (null)}
2017-08-06 12:06:53.989 NSOperationQueue-Example[2325:157825] blockOperation執行第1任務, 當前的執行緒為: <NSThread: 0x608000074180>{number = 4, name = (null)}
2017-08-06 12:06:53.990 NSOperationQueue-Example[2325:157835] invocationOperation執行第2任務, 當前的執行緒為: <NSThread: 0x60000006c540>{number = 3, name = (null)}
複製程式碼
  • 看過上一篇文章的人都知道, 直接使用NSInvocationOperationNSBlockOperation(不使用新增額外任務的方法)只會在主執行緒中執行, 但如果配合著NSOperationQueue來使用的話, 就可以自動開啟子執行緒, 並且是同步執行.

第二種方式

使用下面這個方法來將任務新增到佇列中:

- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);
複製程式碼
- (void)addMissionToPerationQueueBlock {
    
    NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
    
    [operationQueue addOperationWithBlock:^{
        
        for (NSInteger i = 0; i < 3; i++) {
            
            NSLog(@"執行第%zd任務, 當前的執行緒為: %@", i, [NSThread currentThread]);
        }
    }];
}
複製程式碼
2017-08-06 12:13:16.518 NSOperationQueue-Example[2382:165154] 執行第0任務, 當前的執行緒為: <NSThread: 0x60000007c140>{number = 3, name = (null)}
2017-08-06 12:13:16.519 NSOperationQueue-Example[2382:165154] 執行第1任務, 當前的執行緒為: <NSThread: 0x60000007c140>{number = 3, name = (null)}
2017-08-06 12:13:16.519 NSOperationQueue-Example[2382:165154] 執行第2任務, 當前的執行緒為: <NSThread: 0x60000007c140>{number = 3, name = (null)}
複製程式碼
  • 從結果中我們可以看得出, 這個方法更加的簡潔程式碼, 而且也是可以開啟新執行緒並且同步執行任務的.

注意: 這裡還需要注意一下, 將任務新增到佇列中的時候, 我們不需要手動去呼叫- (void)star;方法, 佇列會自動去呼叫.


自由切換序列/並行佇列

剛剛我們演示的都是並行佇列的操作方式, 那如果要求是序列任務咋辦咧? 這時候我們只需要更改一個東西就可以了:

@property NSInteger maxConcurrentOperationCount;
複製程式碼
- (void)changeSyncOrAsyncQueue {
    
    NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
    
    // 如果要序列佇列, 就設定為1, 否則就不設定, 預設並行佇列
    operationQueue.maxConcurrentOperationCount = 1;
    
    [operationQueue addOperationWithBlock:^{
        
        NSLog(@"執行第一個任務, 當前的執行緒為: %@", [NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:0.1];
    }];
    
    [operationQueue addOperationWithBlock:^{
        
        NSLog(@"執行第二個任務, 當前的執行緒為: %@", [NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:0.1];
    }];

    [operationQueue addOperationWithBlock:^{
        
        NSLog(@"執行第三個任務, 當前的執行緒為: %@", [NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:0.1];
    }];
    
    [operationQueue addOperationWithBlock:^{
        
        NSLog(@"執行第四個任務, 當前的執行緒為: %@", [NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:0.1];
    }];

    [operationQueue addOperationWithBlock:^{
        
        NSLog(@"執行第五個任務, 當前的執行緒為: %@", [NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:0.1];
    }];
}
複製程式碼
2017-08-06 12:24:24.786 NSOperationQueue-Example[2571:179165] 執行第一個任務, 當前的執行緒為: <NSThread: 0x6000000771c0>{number = 3, name = (null)}
2017-08-06 12:24:24.887 NSOperationQueue-Example[2571:179162] 執行第二個任務, 當前的執行緒為: <NSThread: 0x600000076840>{number = 4, name = (null)}
2017-08-06 12:24:24.992 NSOperationQueue-Example[2571:179165] 執行第三個任務, 當前的執行緒為: <NSThread: 0x6000000771c0>{number = 3, name = (null)}
2017-08-06 12:24:25.096 NSOperationQueue-Example[2571:179162] 執行第四個任務, 當前的執行緒為: <NSThread: 0x600000076840>{number = 4, name = (null)}
2017-08-06 12:24:25.200 NSOperationQueue-Example[2571:179162] 執行第五個任務, 當前的執行緒為: <NSThread: 0x600000076840>{number = 4, name = (null)}
複製程式碼
  • 從上面的結果可以看得出, 當我們把最大併發數設定為1的時候, 可以看得出是序列執行(並行執行這裡就不演示了)的, 而且開啟的執行緒數也是由系統所決定的.

任務與任務之間的依賴關係

NSOperationNSOperationQueue中, 個人覺得最吸引人的地方, 還是這個依賴關係, 它可以讓指定的任務依賴於另一個任務, 只有當被依賴的任務執行完之後, 才會去執行依賴的任務, 說的有些拗口, 直接來看程式碼吧:

- (void)addDependency {
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *blockOperationOne = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"執行第一次任務, 當前執行緒為: %@", [NSThread currentThread]);
    }];
    
    NSBlockOperation *blockOperationTwo = [NSBlockOperation blockOperationWithBlock:^{

        NSLog(@"執行第二次任務, 當前執行緒為: %@", [NSThread currentThread]);
    }];
    
    [blockOperationTwo addDependency:blockOperationOne];
    
    [queue addOperation:blockOperationOne];
    [queue addOperation:blockOperationTwo];
}
複製程式碼
2017-08-06 13:34:38.095 NSOperationQueue-Example[2962:210009] 執行第一次任務, 當前執行緒為: <NSThread: 0x600000075b80>{number = 3, name = (null)}
2017-08-06 13:34:38.110 NSOperationQueue-Example[2962:210009] 執行第二次任務, 當前執行緒為: <NSThread: 0x600000075b80>{number = 3, name = (null)}
複製程式碼
  • 從結果上來看, 是執行完第一個任務之後, 才會去執行第二個任務, 無論執行多少次, 或者是延遲多少次都一個樣.
  • 而且, 就算第二個任務先加入佇列, 也是會先執行第一個任務, 才會執行第二個任務.

NSOperation的其他方法

  • 可以取消NSOperation的單個操作.
- (void)cancel;
複製程式碼
  • NSOperationQueue提供的方法, 可以取消佇列中所有的操作
- (void)cancelAllOperations;
複製程式碼
  • 設定NSOperationQueuesuspended屬性, 可以暫停和恢復任務的操作, YES代表暫停佇列, NO代表恢復佇列.
@property (getter=isSuspended) BOOL suspended;
複製程式碼
  • NSOperationQueueisSuspended可以判斷任務的操作是暫停還是恢復.

注意: 這裡的暫停任務操作, 指的不是將當前的操作立刻取消, 而是噹噹前這個任務執行完之後, 就不再執行了. 暫停是暫時暫停, 之後可以恢復, 而取消指的是取消所有操作, 就沒法再接著執行剩下的操作.


總結

好了, 關於NSOperation的相關知識, 這裡差不多就講完了, 如果想看更詳細的, 可以去看看NSOperation官方文件, NSOperationQueue官方文件.


工程地址

專案地址: https://github.com/CainRun/iOS-Project-Example/tree/master/NSOperationQueue-Example


最後

碼字很費腦, 看官賞點飯錢可好

微信

支付寶

相關文章