NSOperation陪踩、NSOperationQueue 簡介
- NSOperation律适、NSOperationQueue是基于GCD更高一層的封裝啃奴,完全面向對象
- NSOperation 和 NSOperationQueue 分別對應 GCD 的 任務 和 隊列,但是NSOperation跟GCD的任務還是有一些區(qū)別的
- NSOperation --> 操作, NSOperationQueue --> 隊列
- 操作步驟:
- 將要執(zhí)行的任務封裝到一個 NSOperation 對象中
- 將此任務添加到一個 NSOperationQueue 對象中
一旺遮、NSOperation (操作):
- NSOperation是個抽象類医寿,不能封裝操作栏赴,只能使用它的子類來封裝操作,或者自定義子類來封裝操作靖秩。子類分別是:NSInvocationOperation 和 NSBlockOperation
- 創(chuàng)建一個 Operation 后须眷,需要調用 start 方法來啟動任務,它會默認在當前隊列同步執(zhí)行沟突。當然你也可以在中途取消一個任務花颗,只需要調用其 cancel 方法即可
- 子類NSInvocationOperation
- (void)op1 {
// 1.創(chuàng)建 NSInvocationOperation 對象
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task) object:nil];
// 2.調用 start 方法開始執(zhí)行操作
[op start];
}
- (void)task1 {
for (int i = 0; i < 2; i++) {
sleep(2); // 模擬耗時操作
NSLog(@"task1---%@", [NSThread currentThread]); // 打印當前線程
}
}
輸出結果:
可以看到:在沒有使用NSOperationQueue且在主線程中單獨使用使用子類 NSInvocationOperation執(zhí)行一個操作的情況下,操作是在當前線程執(zhí)行的惠拭,并沒有開啟新線程
- 子類NSBlockOperation
- (void)op2 {
// 1.創(chuàng)建 NSBlockOperation 對象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
sleep(2); // 模擬耗時操作
NSLog(@"op2---%@", [NSThread currentThread]); // 打印當前線程
}
}];
// 2.調用 start 方法開始執(zhí)行操作
[op start];
}
輸出結果:
可以看到:在沒有使用NSOperationQueue扩劝,在主線程中單獨使用 NSBlockOperation執(zhí)行一個操作的情況下,操作是在當前線程執(zhí)行的职辅,并沒有開啟新線程
在NSBlockOperation提供了一個方法addExecutionBlock:棒呛,通過 addExecutionBlock: 方法就可以為NSBlockOperation添加額外的操作。這些操作(包括 blockOperationWithBlock 中的操作)可以在不同的線程中并發(fā)執(zhí)行域携。只有當所有相關的操作已經(jīng)完成執(zhí)行時簇秒,才視為完成。
效果如下:
/**
* 使用子類 NSBlockOperation
* 調用方法 AddExecutionBlock:
*/
- (void)addExecutionBlock {
// 1.創(chuàng)建 NSBlockOperation 對象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op-->%@", [NSThread currentThread]); // 打印當前線程
}];
// 2.添加額外的操作
// 添加多個addExecutionBlock
for (int i = 0; i < 6; i++) {
[op addExecutionBlock:^{
NSLog(@"op%d-->%@", i, [NSThread currentThread]); // 打印當前線程
}];
}
// 3.調用 start 方法開始執(zhí)行操作
[op start];
}
輸出結果:
可以看出:使用子類NSBlockOperation秀鞭,并調用方法addExecutionBlock:的情況下blockOperationWithBlock:方法中的操作 和addExecutionBlock:中的操作是在不同的線程中異步執(zhí)行的趋观。blockOperationWithBlock:中的操作也可能會在其他線程(非當前線程或主線程)中執(zhí)行
注意:ddExecutionBlock: 方法必須在start()方法之前執(zhí)行,否則就會報錯
二锋边、NSOperationQueue (隊列):
- 調用一個NSOperation對象的start()方法來啟動這個任務皱坛,這個默認是同步執(zhí)行的。就算是addExecutionBlock:方法豆巨,也會在當前線程和其他線程中執(zhí)行麸恍,也就是說還是會占用當前線程(從上面打印結果可以看出來)。如果不想這個任務在主線程中執(zhí)行(代碼默認情況下都在主線程中執(zhí)行)這時候就要用到隊列NSOperationQueue 搀矫。
- 按類型來說的話一共有兩種類型:主隊列、其他隊列刻肄。只要添加到隊列瓤球,會自動調用任務的start()方法。
-
主隊列
主隊列是串行隊列敏弃,添加到主隊列的任務都會一個接一個地排著隊在主線程處理(添加額外操作除外addExecutionBlock)// 主隊列獲取方法 NSOperationQueue *queue = [NSOperationQueue mainQueue];
-
自定義隊列(非主隊列)
添加到這種隊列中的操作卦羡,就會自動放到子線程中執(zhí)行,并包含了:串行、并發(fā)功能// 自定義隊列創(chuàng)建方法 NSOperationQueue *queue = [[NSOperationQueue alloc] init];
三绿饵、NSOperation欠肾、NSOperationQueue 使用步驟
- NSOperation 需要配合 NSOperationQueue 來實現(xiàn)多線程。在默認情況下拟赊,NSOperation單獨使用時系統(tǒng)是同步執(zhí)行操作刺桃,配合NSOperationQueue實現(xiàn)異步執(zhí)行
- 實現(xiàn)多線程的方法:
1.利用addOperationWithBlock添加操作到隊列
/**
* 使用 addOperationWithBlock: 將操作加入到操作隊列中
*/
- (void)addOperationWithBlock {
// 1.創(chuàng)建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.使用 addOperationWithBlock: 添加操作到隊列中
for (int i = 0; i < 5; i++) {
[queue addOperationWithBlock:^{
NSLog(@"op%d-->%@", i, [NSThread currentThread]);
}];
}
}
打印結果:
可以看到:此時是開辟了多線程,進行并發(fā)執(zhí)行
2.利用addOperation添加操作到隊列
- (void)operationQueue {
// 1.創(chuàng)建隊列:創(chuàng)建NSOperationQueue對象
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.創(chuàng)建操作:將需要執(zhí)行的操作封裝到NSOperation對象中
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op1-->%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op2-->%@", [NSThread currentThread]);
}];
// 3.將操作加入到隊列中:將NSOperation對象添加到NSOperationQueue對象中
[queue addOperation:op1];
[queue addOperation:op2];
// 可以對以上操作進行順序執(zhí)行:
// 讓op2 依賴于 op1吸祟,則先執(zhí)行op1瑟慈,在執(zhí)行op2
// [op2 addDependency:op1];
// 讓op1 依賴于 op2,則先執(zhí)行op2屋匕,在執(zhí)行op1
[op1 addDependency:op2];
NSLog(@"queue-->%@", [NSThread currentThread]);
}
打印結果:
可以看到:此時是開辟了多線程葛碧,進行并發(fā)執(zhí)行
四、NSOperation过吻、NSOperationQueue的一些其他功能
1.NSOperationQueue 控制串行執(zhí)行进泼、并發(fā)執(zhí)行
通過控制最大并發(fā)操作數(shù):maxConcurrentOperationCount可以達到串行或并發(fā)的效果。
這里maxConcurrentOperationCount控制的不是并發(fā)線程的數(shù)量纤虽,而是一個隊列中同時能并發(fā)執(zhí)行的最大操作數(shù)乳绕。而且一個操作也并非只能在一個線程中運行。
- (void)setMaxConcurrentOperationCount {
// 1.創(chuàng)建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.設置最大并發(fā)操作數(shù)
queue.maxConcurrentOperationCount = 1;
// queue.maxConcurrentOperationCount = 2;
// 3.添加操作
for (int i = 0; i < 5; i++) {
[queue addOperationWithBlock:^{
for (int j = 0; j < 2; j++) {
NSLog(@"queue%d-->%@", i, [NSThread currentThread]);
}
}];
}
}
- 最大并發(fā)數(shù)maxConcurrentOperationCount = 1的時候:操作是按順序串行執(zhí)行的廓推,并且一個操作完成之后刷袍,下一個操作才開始執(zhí)行
- 最大并發(fā)數(shù)maxConcurrentOperationCount = 2的時候:操作是并發(fā)執(zhí)行的,可以同時執(zhí)行兩個操作樊展。但是開啟線程數(shù)量是由系統(tǒng)決定的呻纹,不需要我們來管理
2.NSOperation 操作依賴 (addDependency:)
控制操作之間的執(zhí)行先后順序,通過NSOperation操作的依賴進行設置专缠。
- 設置操作依賴的幾個方法:
// 添加依賴雷酪,使當前操作依賴于操作 op 操作完成后執(zhí)行
- (void)addDependency:(NSOperation *)op;
// 移除依賴,取消當前操作對操作 op 操作的依賴
- (void)removeDependency:(NSOperation *)op;
// 在當前操作開始執(zhí)行之前完成執(zhí)行的所有操作對象數(shù)組
@property (readonly, copy) NSArray<NSOperation *> *dependencies;
- 設置順序涝婉,添加依賴使用方法:
- (void)operationAddDependency {
// 1.創(chuàng)建隊列:創(chuàng)建NSOperationQueue對象
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.創(chuàng)建操作:將需要執(zhí)行的操作封裝到NSOperation對象中
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op1-->%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op2-->%@", [NSThread currentThread]);
}];
// 3.將操作加入到隊列中:將NSOperation對象添加到NSOperationQueue對象中
[queue addOperation:op1];
[queue addOperation:op2];
// 可以對以上操作進行順序執(zhí)行:
// 讓op2 依賴于 op1哥力,則先執(zhí)行op1,在執(zhí)行op2
// [op2 addDependency:op1];
// 讓op1 依賴于 op2墩弯,則先執(zhí)行op2吩跋,在執(zhí)行op1
[op1 addDependency:op2];
NSLog(@"queue-->%@", [NSThread currentThread]);
}
3.NSOperation 優(yōu)先級
NSOperation 提供了queuePriority(優(yōu)先級)屬性,queuePriority屬性適用于同一操作隊列中的操作渔工,不適用于不同操作隊列中的操作锌钮。默認情況下,所有新創(chuàng)建的操作對象優(yōu)先級都是NSOperationQueuePriorityNormal引矩。但是我們可以通過setQueuePriority:方法來改變當前操作在同一隊列中的執(zhí)行優(yōu)先級
優(yōu)先級不能取代依賴關系
// 優(yōu)先級枚舉值
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
4.暫停和恢復以及取消
- 設置暫停和恢復
suspended設置為YES表示暫停梁丘,suspended設置為NO表示恢復
暫停表示不繼續(xù)執(zhí)行隊列中的下一個任務侵浸,暫停操作是可以恢復的 - 取消隊列里面的所有操作
取消之后,當前正在執(zhí)行的操作的下一個操作將不再執(zhí)行氛谜,而且永遠都不在執(zhí)行掏觉,就像后面的所有任務都從隊列里面移除了一樣
取消操作是不可以恢復的 - 查看否事已經(jīng)取消:isCancelled
蘋果官方建議,每當執(zhí)行完一次耗時操作之后值漫,就查看一下當前隊列是否為取消狀態(tài)澳腹,如果是,那么就直接退出