NSOperation施流、NSOperationQueue 是蘋果提供給我們的一套多線程解決方案。實際上 NSOperation确憨、NSOperationQueue 是基于 GCD 更高一層的封裝译荞,完全面向?qū)ο蟆5潜?GCD 更簡單易用休弃、代碼可讀性也更高吞歼。
1.NSOperation基本使用
NSOperation可以調(diào)用start方法來執(zhí)行任務(wù),但默認是同步執(zhí)行的
NSOperation是個抽象類塔猾,并不具備封裝操作的能力篙骡,必須使用它的子類(在第三條介紹子類)
- 先將需要執(zhí)行的操作封裝到一個NSOperation對象中
- 然后將NSOperation對象添加到NSOperationQueue中
- 系統(tǒng)會自動將NSOperationQueue中的NSOperation取出來
- 將取出的NSOperation封裝的操作放到一條新線程中執(zhí)行
2.NSOperationQueue 隊列
//創(chuàng)建操作(任務(wù))
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSInvocationOperation *operationInvocation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(downLoad1) object:nil];
NSBlockOperation *operationblock = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"downLoad3-----%@",[NSThread currentThread]);
}];
//添加任務(wù)
[queue addOperation:operationInvocation];//自動調(diào)用 [op start]
[queue addOperation:operationblock];//自動調(diào)用 [op start]
- 通過 [[NSOperationQueue alloc]init]創(chuàng)建的操作隊列,其內(nèi)添加的任務(wù)在子線程執(zhí)行。
- 通過[NSOperationQueue mainQueue] 獲取的是主操作隊列糯俗,其內(nèi)添加的任務(wù)(NSOperation)在主線程執(zhí)行尿褪。
- 如果將NSOpeeration添加到NSOperationQueue(操作隊列)中,系統(tǒng)自動異步執(zhí)行NSOperation中的操作得湘。
添加操作到NSOperationQueue中
//add后的任務(wù)不需要調(diào)用-start
就會自動執(zhí)行
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock
3.NSOperation的子類
3.1 NSInvocationOperation
//如果是在主線程創(chuàng)建杖玲,就在主線程執(zhí)行任務(wù)。如果是子線程忽刽,那就在子線程
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
[operation start];
- 默認情況下天揖,調(diào)用了start方法后并不會開一條新線程去執(zhí)行操作,而是在當(dāng)前線程同步執(zhí)行操作
- 只有將NSOperation放到一個NSOperationQueue中跪帝,才會異步執(zhí)行操作
3.2NSBlockOperation
//包裝的任務(wù)在主線程
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1 --- %@",[NSThread currentThread]);
}];
//再添加的任務(wù) 會在新子線程執(zhí)行
[operation addExecutionBlock:^{
NSLog(@"2 --- %@",[NSThread currentThread]);
}];
[operation start];
- 只要NSBlockOperation封裝的操作數(shù) > 1今膊,就會異步執(zhí)行操作
3.3自定義NSOperation
- 自定義NSOperation的步驟很簡單,只要重寫
- (void)main
方法伞剑,在里面實現(xiàn)想執(zhí)行的任務(wù)斑唬。當(dāng)然如果結(jié)構(gòu)很復(fù)雜的自定義NSOperation還需要實現(xiàn)- (BOOL)isConcurrent
是否并發(fā)等方法。 - 經(jīng)常通過- (BOOL)isCancelled方法檢測操作是否被取消黎泣,對取消做出響應(yīng)
//需要執(zhí)行的任務(wù)
- (void)main{
for (int i =0; i<10088; i++) {
NSLog(@"XBSOpertion----下載任務(wù)%@",[NSThread currentThread]);
}
//這句寫與否直接就看出你的水平
if (self.isCancelled) return;
for (int i =0; i<10088; i++) {
NSLog(@"XBSOpertion----下載任務(wù)%@",[NSThread currentThread]);
}
}
4.最大并發(fā)數(shù)maxConcurrentOperationCount
系統(tǒng)默認最大并發(fā)數(shù)是NSOperationQueueDefaultMaxConcurrentOperationCount = -1
.
- maxConcurrentOperationCount = 0 不執(zhí)行隊列
- maxConcurrentOperationCount = 1 串行隊列
- maxConcurrentOperationCount > 1 并發(fā)隊列
5.NSOperation掛起和取消
注意:
1.這里的暫停和取消(包括操作的取消和隊列的取消)并不代表可以將當(dāng)前的操作立即取消恕刘,而是當(dāng)當(dāng)前的操作執(zhí)行完畢之后不再執(zhí)行新的操作。
2.暫停和取消的區(qū)別就在于:暫停操作之后還可以恢復(fù)操作抒倚,繼續(xù)向下執(zhí)行褐着;而取消操作之后,所有的操作就清空了托呕,無法再接著執(zhí)行剩下的操作
5.1掛起suspended.
- suspended YES:暫停(掛起)隊列 NO:恢復(fù)隊列
5.2 取消
- 單個任務(wù)取消 [operation cancel];
- 整個操作隊列所有未開始任務(wù)的取消 [self.queue cancelAllOperations]
6.依賴和監(jiān)聽
6.1依賴[queue addExecution]
6.1.1 同隊列內(nèi)依賴
NSOperation之間可以設(shè)置依賴來保證執(zhí)行順序
比如一定要讓操作A執(zhí)行完后含蓉,才能執(zhí)行操作B,可以這么寫
[operationB addDependency:operationA]; // 操作B依賴于操作A
6.1.2 跨隊列依賴
可以在不同queue的NSOperation之間創(chuàng)建依賴關(guān)系
圖解:NSOperation3依賴于NSOperation2项郊,NSOperation2又依賴于其他隊列的NSOperation6馅扣,NSOperation6又依賴于NSOperation1。
只要是個NSOperation對象就可以建立依賴着降,非常強大差油。
注意:不能相互依賴。比如A依賴B任洞,B依賴A
6.2 監(jiān)聽
可以監(jiān)聽一個操作的執(zhí)行完畢
- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;
注意點:蘋果官方描述:
A finished (or canceled) operation is still given a chance to execute its completion block before it is removed from the queue
一個已經(jīng)完成(或者已經(jīng)被取消)的操作在被移除操作隊列之前仍然有機會去執(zhí)行completion block蓄喇。
7.知識實踐
下載兩張圖片,然后合成一張顯示處出啦
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSOperationQueue *queueOp = [[NSOperationQueue alloc]init];
__block UIImage *image1 = nil;
//下載圖片1
NSBlockOperation *downLoad1 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"https://upload-images.jianshu.io/upload_images/6687791-5ad29d1a5c1c84db.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"];
image1 = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
}];
__block UIImage *image2 = nil;
NSBlockOperation *downLoad2 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"https://upload-images.jianshu.io/upload_images/6687791-2a51b4b27040ed41.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"];
image2 = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
}];
//4.等待兩張圖片下載完成交掏,將兩張圖片合成一張
NSBlockOperation *combine = [NSBlockOperation blockOperationWithBlock:^{
//開啟新的圖形上下文
UIGraphicsBeginImageContext(CGSizeMake(300, 300));
//繪制圖片
[image1 drawInRect:CGRectMake(0, 0, 300, 150)];
image1 = nil;
[image2 drawInRect:CGRectMake(0, 150, 300, 150)];
image2 = nil;
//取得圖形上下文中的圖片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
//結(jié)束上下文
UIGraphicsEndImageContext();
//回到主線程顯示圖片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = newImage;
}];
}];
//combine合成任務(wù)的執(zhí)行依賴 downLoad1 和 downLoad2的執(zhí)行完成
[combine addDependency:downLoad1];
[combine addDependency:downLoad2];
//三個任務(wù)添加到操作隊列中
[queueOp addOperation:downLoad1];
[queueOp addOperation:downLoad2];
[queueOp addOperation:combine];
}