文章分享至我的個人技術部落格: https://cainluo.github.io/15019907895337.html
在上一章, 我們把NSOperation
的一些基礎概念和一些簡單的用法給摸了一遍, 但在示例當中, 我們發現了只單獨使用NSOperation
的話, 只會在主執行緒裡執行, 雖然NSBlockOperation
額外可以新增任務在子執行緒裡執行, 但這還是不夠滴, 這次我們接下來就把NSOperation
和NSOperationQueue
配合使用給補全.
如果沒有看過上一篇文章的話, 可以去看看玩轉iOS開發:iOS中的NSOperation開發(一).
轉載宣告:如需要轉載該文章, 請聯絡作者, 並且註明出處, 以及不能擅自修改本文.
NSOperationQueue
NSOperationQueue
和GCD
的佇列有些區別, 在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)}
複製程式碼
- 看過上一篇文章的人都知道, 直接使用
NSInvocationOperation
和NSBlockOperation
(不使用新增額外任務的方法)只會在主執行緒中執行, 但如果配合著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
的時候, 可以看得出是序列執行
(並行執行
這裡就不演示了)的, 而且開啟的執行緒數也是由系統所決定的.
任務與任務之間的依賴關係
在NSOperation
和NSOperationQueue
中, 個人覺得最吸引人的地方, 還是這個依賴關係, 它可以讓指定的任務依賴於另一個任務, 只有當被依賴的任務執行完之後, 才會去執行依賴的任務, 說的有些拗口, 直接來看程式碼吧:
- (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;
複製程式碼
- 設定
NSOperationQueue
的suspended
屬性, 可以暫停和恢復任務的操作,YES
代表暫停佇列,NO
代表恢復佇列.
@property (getter=isSuspended) BOOL suspended;
複製程式碼
NSOperationQueue
的isSuspended
可以判斷任務的操作是暫停還是恢復.
注意: 這裡的暫停任務操作, 指的不是將當前的操作立刻取消, 而是噹噹前這個任務執行完之後, 就不再執行了. 暫停是暫時暫停, 之後可以恢復, 而取消指的是取消所有操作, 就沒法再接著執行剩下的操作.
總結
好了, 關於NSOperation
的相關知識, 這裡差不多就講完了, 如果想看更詳細的, 可以去看看NSOperation官方文件, NSOperationQueue官方文件.
工程地址
專案地址: https://github.com/CainRun/iOS-Project-Example/tree/master/NSOperationQueue-Example