筆者對以前寫的原文從新整理,修改了部份內容,又增長了許多關於 NSOperation、NSOperationQueue 的知識及用法,但願你們喜歡。html
本文用來介紹 iOS 多線程中 NSOperation、NSOperationQueue 的相關知識以及使用方法。git
經過本文,您將瞭解到:
NSOperation、NSOperationQueue 簡介、操做和操做隊列、使用步驟和基本使用方法、控制串行/併發執行、NSOperation 操做依賴和優先級、線程間的通訊、線程同步和線程安全,以及 NSOperation、NSOperationQueue 經常使用屬性和方法概括。github文中 Demo 我已放在了 Github 上,Demo 連接:傳送門編程
NSOperation、NSOperationQueue 是蘋果提供給咱們的一套多線程解決方案。實際上 NSOperation、NSOperationQueue 是基於 GCD 更高一層的封裝,徹底面向對象。可是比 GCD 更簡單易用、代碼可讀性也更高。數組
爲何要使用 NSOperation、NSOperationQueue?安全
既然是基於 GCD 的更高一層的封裝。那麼,GCD 中的一些概念一樣適用於 NSOperation、NSOperationQueue。在 NSOperation、NSOperationQueue 中也有相似的任務(操做)和隊列(操做隊列)的概念。多線程
操做(Operation):併發
操做隊列(Operation Queues):app
NSOperation 須要配合 NSOperationQueue 來實現多線程。由於默認狀況下,NSOperation 單獨使用時系統同步執行操做,配合 NSOperationQueue 咱們能更好的實現異步執行。異步
NSOperation 實現多線程的使用步驟分爲三步:
以後呢,系統就會自動將 NSOperationQueue 中的 NSOperation 取出來,在新線程中執行操做。
下面咱們來學習下 NSOperation 和 NSOperationQueue 的基本使用。
NSOperation 是個抽象類,不能用來封裝操做。咱們只有使用它的子類來封裝操做。咱們有三種方式來封裝操做。
在不使用 NSOperationQueue,單獨使用 NSOperation 的狀況下系統同步執行操做,下面咱們學習如下操做的三種建立方式。
NSInvocationOperation
/** * 使用子類 NSInvocationOperation */ - (void)useInvocationOperation { // 1.建立 NSInvocationOperation 對象 NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil]; // 2.調用 start 方法開始執行操做 [op start]; } /** * 任務1 */ - (void)task1 { for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程 } }
輸出結果:
若是在其餘線程中執行操做,則打印結果爲其餘線程。
// 在其餘線程使用子類 NSInvocationOperation [NSThread detachNewThreadSelector:@selector(useInvocationOperation) toTarget:self withObject:nil];
輸出結果:
下邊再來看看 NSBlockOperation。
NSBlockOperation
/** * 使用子類 NSBlockOperation */ - (void)useBlockOperation { // 1.建立 NSBlockOperation 對象 NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程 } }]; // 2.調用 start 方法開始執行操做 [op start]; }
輸出結果:
注意:和上邊 NSInvocationOperation 使用同樣。由於代碼是在主線程中調用的,因此打印結果爲主線程。若是在其餘線程中執行操做,則打印結果爲其餘線程。
可是,NSBlockOperation 還提供了一個方法 addExecutionBlock:
,經過 addExecutionBlock:
就能夠爲 NSBlockOperation 添加額外的操做。這些操做(包括 blockOperationWithBlock 中的操做)能夠在不一樣的線程中同時(併發)執行。只有當全部相關的操做已經完成執行時,才視爲完成。
若是添加的操做多的話, blockOperationWithBlock:
中的操做也可能會在其餘線程(非當前線程)中執行,這是由系統決定的,並非說添加到 blockOperationWithBlock:
中的操做必定會在當前線程中執行。(可使用 addExecutionBlock:
多添加幾個操做試試)。
/** * 使用子類 NSBlockOperation * 調用方法 AddExecutionBlock: */ - (void)useBlockOperationAddExecutionBlock { // 1.建立 NSBlockOperation 對象 NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程 } }]; // 2.添加額外的操做 [op addExecutionBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"2---%@", [NSThread currentThread]); // 打印當前線程 } }]; [op addExecutionBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"3---%@", [NSThread currentThread]); // 打印當前線程 } }]; [op addExecutionBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"4---%@", [NSThread currentThread]); // 打印當前線程 } }]; [op addExecutionBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"5---%@", [NSThread currentThread]); // 打印當前線程 } }]; [op addExecutionBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"6---%@", [NSThread currentThread]); // 打印當前線程 } }]; [op addExecutionBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"7---%@", [NSThread currentThread]); // 打印當前線程 } }]; [op addExecutionBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"8---%@", [NSThread currentThread]); // 打印當前線程 } }]; // 3.調用 start 方法開始執行操做 [op start]; }
輸出結果:
NSBlockOperation
,並調用方法 AddExecutionBlock:
的狀況下,blockOperationWithBlock:
方法中的操做 和 addExecutionBlock:
中的操做是在不一樣的線程中異步執行的。並且,此次執行結果中 blockOperationWithBlock:
方法中的操做也不是在當前線程(主線程)中執行的。從而印證了 blockOperationWithBlock:
中的操做也可能會在其餘線程(非當前線程)中執行。通常狀況下,若是一個 NSBlockOperation 對象封裝了多個操做。NSBlockOperation 是否開啓新線程,取決於操做的個數。若是添加的操做的個數多,就會自動開啓新線程。固然開啓的線程數是由系統來決定的。
若是使用子類 NSInvocationOperation、NSBlockOperation 不能知足平常需求,咱們可使用自定義繼承自 NSOperation 的子類。能夠經過重寫 main
或者 start
方法 來定義本身的 NSOperation 對象。重寫main
方法比較簡單,咱們不須要管理操做的狀態屬性 isExecuting
和 isFinished
。當 main
執行完返回的時候,這個操做就結束了。
先定義一個繼承自 NSOperation 的子類,重寫main
方法。
// YSCOperation.h 文件 #import <Foundation/Foundation.h> @interface YSCOperation : NSOperation @end // YSCOperation.m 文件 #import "YSCOperation.h" @implementation YSCOperation - (void)main { if (!self.isCancelled) { for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"1---%@", [NSThread currentThread]); } } } @end
而後使用的時候導入頭文件YSCOperation.h
。
/** * 使用自定義繼承自 NSOperation 的子類 */ - (void)useCustomOperation { // 1.建立 YSCOperation 對象 YSCOperation *op = [[YSCOperation alloc] init]; // 2.調用 start 方法開始執行操做 [op start]; }
輸出結果:
下邊咱們來說講 NSOperationQueue 的建立。
NSOperationQueue 一共有兩種隊列:主隊列、自定義隊列。其中自定義隊列同時包含了串行、併發功能。下邊是主隊列、自定義隊列的基本建立方法和特色。
主隊列
// 主隊列獲取方法 NSOperationQueue *queue = [NSOperationQueue mainQueue];
自定義隊列(非主隊列)
// 自定義隊列建立方法 NSOperationQueue *queue = [[NSOperationQueue alloc] init];
上邊咱們說到 NSOperation 須要配合 NSOperationQueue 來實現多線程。
那麼咱們須要將建立好的操做加入到隊列中去。總共有兩種方法:
- (void)addOperation:(NSOperation *)op;
/** * 使用 addOperation: 將操做加入到操做隊列中 */ - (void)addOperationToQueue { // 1.建立隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.建立操做 // 使用 NSInvocationOperation 建立操做1 NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil]; // 使用 NSInvocationOperation 建立操做2 NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil]; // 使用 NSBlockOperation 建立操做3 NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"3---%@", [NSThread currentThread]); // 打印當前線程 } }]; [op3 addExecutionBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"4---%@", [NSThread currentThread]); // 打印當前線程 } }]; // 3.使用 addOperation: 添加全部操做到隊列中 [queue addOperation:op1]; // [op1 start] [queue addOperation:op2]; // [op2 start] [queue addOperation:op3]; // [op3 start] }
輸出結果:
addOperation:
將操做加入到操做隊列後可以開啓新線程,進行併發執行。- (void)addOperationWithBlock:(void (^)(void))block;
/** * 使用 addOperationWithBlock: 將操做加入到操做隊列中 */ - (void)addOperationWithBlockToQueue { // 1.建立隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.使用 addOperationWithBlock: 添加操做到隊列中 [queue addOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程 } }]; [queue addOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"2---%@", [NSThread currentThread]); // 打印當前線程 } }]; [queue addOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"3---%@", [NSThread currentThread]); // 打印當前線程 } }]; }
輸出結果:
以前咱們說過,NSOperationQueue 建立的自定義隊列同時具備串行、併發功能,上邊咱們演示了併發功能,那麼他的串行功能是如何實現的?
這裏有個關鍵屬性 maxConcurrentOperationCount
,叫作最大併發操做數。用來控制一個特定隊列中能夠有多少個操做同時參與併發執行。
注意:這裏
maxConcurrentOperationCount
控制的不是併發線程的數量,而是一個隊列中同時能併發執行的最大操做數。並且一個操做也並不是只能在一個線程中運行。
最大併發操做數:maxConcurrentOperationCount
maxConcurrentOperationCount
默認狀況下爲-1,表示不進行限制,可進行併發執行。maxConcurrentOperationCount
爲1時,隊列爲串行隊列。只能串行執行。maxConcurrentOperationCount
大於1時,隊列爲併發隊列。操做併發執行,固然這個值不該超過系統限制,即便本身設置一個很大的值,系統也會自動調整爲 min{本身設定的值,系統設定的默認最大值}。/** * 設置 MaxConcurrentOperationCount(最大併發操做數) */ - (void)setMaxConcurrentOperationCount { // 1.建立隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.設置最大併發操做數 queue.maxConcurrentOperationCount = 1; // 串行隊列 // queue.maxConcurrentOperationCount = 2; // 併發隊列 // queue.maxConcurrentOperationCount = 8; // 併發隊列 // 3.添加操做 [queue addOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程 } }]; [queue addOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"2---%@", [NSThread currentThread]); // 打印當前線程 } }]; [queue addOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"3---%@", [NSThread currentThread]); // 打印當前線程 } }]; [queue addOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"4---%@", [NSThread currentThread]); // 打印當前線程 } }]; }
這樣看來,是否是比 GCD 還要簡單了許多?
NSOperation、NSOperationQueue 最吸引人的地方是它能添加操做之間的依賴關係。經過操做依賴,咱們能夠很方便的控制操做之間的執行前後順序。NSOperation 提供了3個接口供咱們管理和查看依賴。
- (void)addDependency:(NSOperation *)op;
添加依賴,使當前操做依賴於操做 op 的完成。- (void)removeDependency:(NSOperation *)op;
移除依賴,取消當前操做對操做 op 的依賴。@property (readonly, copy) NSArray<NSOperation *> *dependencies;
在當前操做開始執行以前完成執行的全部操做對象數組。固然,咱們常常用到的仍是添加依賴操做。如今考慮這樣的需求,好比說有 A、B 兩個操做,其中 A 執行完操做,B 才能執行操做。
若是使用依賴來處理的話,那麼就須要讓操做 B 依賴於操做 A。具體代碼以下:
/** * 操做依賴 * 使用方法:addDependency: */ - (void)addDependency { // 1.建立隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.建立操做 NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程 } }]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"2---%@", [NSThread currentThread]); // 打印當前線程 } }]; // 3.添加依賴 [op2 addDependency:op1]; // 讓op2 依賴於 op1,則先執行op1,在執行op2 // 4.添加操做到隊列中 [queue addOperation:op1]; [queue addOperation:op2]; }
輸出結果:
NSOperation 提供了queuePriority
(優先級)屬性,queuePriority
屬性適用於同一操做隊列中的操做,不適用於不一樣操做隊列中的操做。默認狀況下,全部新建立的操做對象優先級都是NSOperationQueuePriorityNormal
。可是咱們能夠經過setQueuePriority:
方法來改變當前操做在同一隊列中的執行優先級。
// 優先級的取值 typedef NS_ENUM(NSInteger, NSOperationQueuePriority) { NSOperationQueuePriorityVeryLow = -8L, NSOperationQueuePriorityLow = -4L, NSOperationQueuePriorityNormal = 0, NSOperationQueuePriorityHigh = 4, NSOperationQueuePriorityVeryHigh = 8 };
上邊咱們說過:對於添加到隊列中的操做,首先進入準備就緒的狀態(就緒狀態取決於操做之間的依賴關係),而後進入就緒狀態的操做的開始執行順序(非結束執行順序)由操做之間相對的優先級決定(優先級是操做對象自身的屬性)。
那麼,什麼樣的操做纔是進入就緒狀態的操做呢?
舉個例子,如今有4個優先級都是 NSOperationQueuePriorityNormal
(默認級別)的操做:op1,op2,op3,op4。其中 op3 依賴於 op2,op2 依賴於 op1,即 op3 -> op2 -> op1。如今將這4個操做添加到隊列中併發執行。
理解了進入就緒狀態的操做,那麼咱們就理解了queuePriority
屬性的做用對象。
queuePriority
屬性決定了進入準備就緒狀態下的操做之間的開始執行順序。而且,優先級不能取代依賴關係。在 iOS 開發過程當中,咱們通常在主線程裏邊進行 UI 刷新,例如:點擊、滾動、拖拽等事件。咱們一般把一些耗時的操做放在其餘線程,好比說圖片下載、文件上傳等耗時操做。而當咱們有時候在其餘線程完成了耗時操做時,須要回到主線程,那麼就用到了線程之間的通信。
/** * 線程間通訊 */ - (void)communication { // 1.建立隊列 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; // 2.添加操做 [queue addOperationWithBlock:^{ // 異步進行耗時操做 for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程 } // 回到主線程 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // 進行一些 UI 刷新等操做 for (int i = 0; i < 2; i++) { [NSThread sleepForTimeInterval:2]; // 模擬耗時操做 NSLog(@"2---%@", [NSThread currentThread]); // 打印當前線程 } }]; }]; }
輸出結果:
若每一個線程中對全局變量、靜態變量只有讀操做,而無寫操做,通常來講,這個全局變量是線程安全的;如有多個線程同時執行寫操做(更改變量),通常都須要考慮線程同步,不然的話就可能影響線程安全。
舉個簡單例子就是:兩我的在一塊兒聊天。兩我的不能同時說話,避免聽不清(操做衝突)。等一我的說完(一個線程結束操做),另外一個再說(另外一個線程再開始操做)。
下面,咱們模擬火車票售賣的方式,實現 NSOperation 線程安全和解決線程同步問題。
場景:總共有50張火車票,有兩個售賣火車票的窗口,一個是北京火車票售賣窗口,另外一個是上海火車票售賣窗口。兩個窗口同時售賣火車票,賣完爲止。
先來看看不考慮線程安全的代碼:
/** * 非線程安全:不使用 NSLock * 初始化火車票數量、賣票窗口(非線程安全)、並開始賣票 */ - (void)initTicketStatusNotSave { NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當前線程 self.ticketSurplusCount = 50; // 1.建立 queue1,queue1 表明北京火車票售賣窗口 NSOperationQueue *queue1 = [[NSOperationQueue alloc] init]; queue1.maxConcurrentOperationCount = 1; // 2.建立 queue2,queue2 表明上海火車票售賣窗口 NSOperationQueue *queue2 = [[NSOperationQueue alloc] init]; queue2.maxConcurrentOperationCount = 1; // 3.建立賣票操做 op1 __weak typeof(self) weakSelf = self; NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ [weakSelf saleTicketNotSafe]; }]; // 4.建立賣票操做 op2 NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ [weakSelf saleTicketNotSafe]; }]; // 5.添加操做,開始賣票 [queue1 addOperation:op1]; [queue2 addOperation:op2]; } /** * 售賣火車票(非線程安全) */ - (void)saleTicketNotSafe { while (1) { if (self.ticketSurplusCount > 0) { //若是還有票,繼續售賣 self.ticketSurplusCount--; NSLog(@"%@", [NSString stringWithFormat:@"剩餘票數:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]); [NSThread sleepForTimeInterval:0.2]; } else { NSLog(@"全部火車票均已售完"); break; } } }
輸出結果:
省略一部分結果圖。。。
線程安全解決方案:能夠給線程加鎖,在一個線程執行該操做的時候,不容許其餘線程進行操做。iOS 實現線程加鎖有不少種方式。@synchronized、 NSLock、NSRecursiveLock、NSCondition、NSConditionLock、pthread_mutex、dispatch_semaphore、OSSpinLock、atomic(property) set/ge等等各類方式。這裏咱們使用 NSLock 對象來解決線程同步問題。NSLock 對象能夠經過進入鎖時調用 lock 方法,解鎖時調用 unlock 方法來保證線程安全。
考慮線程安全的代碼:
/** * 線程安全:使用 NSLock 加鎖 * 初始化火車票數量、賣票窗口(線程安全)、並開始賣票 */ - (void)initTicketStatusSave { NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當前線程 self.ticketSurplusCount = 50; self.lock = [[NSLock alloc] init]; // 初始化 NSLock 對象 // 1.建立 queue1,queue1 表明北京火車票售賣窗口 NSOperationQueue *queue1 = [[NSOperationQueue alloc] init]; queue1.maxConcurrentOperationCount = 1; // 2.建立 queue2,queue2 表明上海火車票售賣窗口 NSOperationQueue *queue2 = [[NSOperationQueue alloc] init]; queue2.maxConcurrentOperationCount = 1; // 3.建立賣票操做 op1 __weak typeof(self) weakSelf = self; NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ [weakSelf saleTicketSafe]; }]; // 4.建立賣票操做 op2 NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ [weakSelf saleTicketSafe]; }]; // 5.添加操做,開始賣票 [queue1 addOperation:op1]; [queue2 addOperation:op2]; } /** * 售賣火車票(線程安全) */ - (void)saleTicketSafe { while (1) { // 加鎖 [self.lock lock]; if (self.ticketSurplusCount > 0) { //若是還有票,繼續售賣 self.ticketSurplusCount--; NSLog(@"%@", [NSString stringWithFormat:@"剩餘票數:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]); [NSThread sleepForTimeInterval:0.2]; } // 解鎖 [self.lock unlock]; if (self.ticketSurplusCount <= 0) { NSLog(@"全部火車票均已售完"); break; } } }
輸出結果:
省略一部分結果圖。。。
取消操做方法
- (void)cancel;
可取消操做,實質是標記 isCancelled 狀態。判斷操做狀態方法
- (BOOL)isFinished;
判斷操做是否已經結束。- (BOOL)isCancelled;
判斷操做是否已經標記爲取消。- (BOOL)isExecuting;
判斷操做是否正在在運行。- (BOOL)isReady;
判斷操做是否處於準備就緒狀態,這個值和操做的依賴關係相關。操做同步
- (void)waitUntilFinished;
阻塞當前線程,直到該操做結束。可用於線程執行順序的同步。- (void)setCompletionBlock:(void (^)(void))block;
completionBlock
會在當前操做執行完畢時執行 completionBlock。- (void)addDependency:(NSOperation *)op;
添加依賴,使當前操做依賴於操做 op 的完成。- (void)removeDependency:(NSOperation *)op;
移除依賴,取消當前操做對操做 op 的依賴。@property (readonly, copy) NSArray<NSOperation *> *dependencies;
在當前操做開始執行以前完成執行的全部操做對象數組。取消/暫停/恢復操做
- (void)cancelAllOperations;
能夠取消隊列的全部操做。- (BOOL)isSuspended;
判斷隊列是否處於暫停狀態。 YES 爲暫停狀態,NO 爲恢復狀態。- (void)setSuspended:(BOOL)b;
可設置操做的暫停和恢復,YES 表明暫停隊列,NO 表明恢復隊列。操做同步
- (void)waitUntilAllOperationsAreFinished;
阻塞當前線程,直到隊列中的操做所有執行完畢。添加/獲取操做`
- (void)addOperationWithBlock:(void (^)(void))block;
向隊列中添加一個 NSBlockOperation 類型操做對象。- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait;
向隊列中添加操做數組,wait 標誌是否阻塞當前線程直到全部操做結束- (NSArray *)operations;
當前在隊列中的操做數組(某個操做執行結束後會自動從這個數組清除)。- (NSUInteger)operationCount;
當前隊列中的操做數。獲取隊列
+ (id)currentQueue;
獲取當前隊列,若是當前線程不是在 NSOperationQueue 上運行則返回 nil。+ (id)mainQueue;
獲取主隊列。注意:
- 這裏的暫停和取消(包括操做的取消和隊列的取消)並不表明能夠將當前的操做當即取消,而是噹噹前的操做執行完畢以後再也不執行新的操做。
- 暫停和取消的區別就在於:暫停操做以後還能夠恢復操做,繼續向下執行;而取消操做以後,全部的操做就清空了,沒法再接着執行剩下的操做。
參考資料: