上一節(jié)中,我們探究了GCD實(shí)現(xiàn)多線程的各種方式,有圖有真相,不清楚的朋友們可以回去看一看啦。這一節(jié)中,我們來(lái)看看蘋(píng)果官方給我們提供的又一個(gè)實(shí)現(xiàn)多線程的方式,NSOperation。
GCD鏈接:iOS詳解多線程(實(shí)現(xiàn)篇——GCD)
NSThread鏈接:詳解多線程(實(shí)現(xiàn)篇——NSThread)
多線程概念篇鏈接:詳解多線程(概念篇——進(jìn)程、線程以及多線程原理)
源碼鏈接:https://github.com/weiman152/Multithreading.git
多線程的實(shí)現(xiàn)方法
3.NSOperation(OC)
4.C語(yǔ)言的pthread(C語(yǔ)言)
5.其他實(shí)現(xiàn)多線程方法
本節(jié)主要內(nèi)容
- NSOperation是什么
- NSOperation的使用
2_1.NSOperation對(duì)象的創(chuàng)建(三種方式)
2_2. NSOperationQueue的使用
2_3. 任務(wù)和隊(duì)列結(jié)合使用- NSOperation其他重要用法
3_1.NSOperation的依賴(lài)- 案例
1.NSOperation是什么
NSOperation是蘋(píng)果官方提供的面向?qū)ο蟮囊环N解決多線程的方式。NSOperation是對(duì)GCD的封裝。我們已經(jīng)知道,GCD是C語(yǔ)言風(fēng)格的,NSOperation是OC的面向?qū)ο蟮模菺CD多了一些更加簡(jiǎn)單實(shí)用的功能,使用起來(lái)更加的便捷,容易理解。
NSOperation 和NSOperationQueue 分別對(duì)應(yīng) GCD 的 任務(wù) 和 隊(duì)列。
2.NSOperation的使用
我們現(xiàn)在知道,NSOperation是對(duì)GCD的封裝,復(fù)習(xí)一下GCD的使用步驟:
1.創(chuàng)建隊(duì)列(串行、并發(fā));
2.創(chuàng)建任務(wù)(同步、異步);
把任務(wù)放在隊(duì)列中執(zhí)行。
NSOperation的使用也是差不多的步驟:
1.將任務(wù)封裝到NSOperation對(duì)象中;
2.將NSOperation對(duì)象添加到NSOperationQueue中;
系統(tǒng)會(huì)自動(dòng)將NSOperationQueue中的NSOperation取出來(lái),并將取出的NSOperation封裝的操作放到一條新線程中執(zhí)行。
2.1 NSOperation對(duì)象的創(chuàng)建
注意:NSOperation是一個(gè)抽象類(lèi),不具備封裝操作的能力,必須使用它的子類(lèi)。
NSOperation有三個(gè)子類(lèi)都可以創(chuàng)建任務(wù)對(duì)象。
1》NSInvocationOperation
先創(chuàng)建一個(gè)待執(zhí)行的函數(shù),run
-(void)runWithName:(NSString *)name{
NSLog(@"-------%@-------",name);
NSLog(@"當(dāng)前線程:%@",[NSThread currentThread]);
NSLog(@"哈哈哈哈,我是個(gè)任務(wù),要執(zhí)行2秒哦");
[NSThread sleepForTimeInterval:2.0];
NSLog(@"任務(wù)結(jié)束啦");
}
NSInvocationOperation執(zhí)行run方法。
- (IBAction)test1:(id)sender {
//1.NSInvocationOperation
NSInvocationOperation * invocationOp = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(runWithName:) object:@"NSInvocationOperation"];
//啟動(dòng)
[invocationOp start];
}
結(jié)果:
2》NSBlockOperation
NSBlockOperation是最常用的一種方式了,可以追加任務(wù),并且追加的任務(wù)是在子線程中執(zhí)行的。
- (IBAction)test2:(id)sender {
//2.NSBlockOperation(最常使用)
NSBlockOperation * blockOp = [NSBlockOperation blockOperationWithBlock:^{
//要執(zhí)行的操作,目前是主線程
NSLog(@"NSBlockOperation 創(chuàng)建,線程:%@",[NSThread currentThread]);
}];
//2.1 追加任務(wù),在子線程中執(zhí)行
[blockOp addExecutionBlock:^{
NSLog(@"追加任務(wù)一");
[self runWithName:@"NSBlockOperation 追加"];
}];
[blockOp addExecutionBlock:^{
NSLog(@"追加任務(wù)二, %@",[NSThread currentThread]);
}];
[blockOp start];
}
結(jié)果:
3》自定義類(lèi)繼承自NSOperation,實(shí)現(xiàn)main方法。
我們創(chuàng)建一個(gè)類(lèi)WMOperation繼承自NSOperation,如下圖:
實(shí)現(xiàn)main方法:
使用:
- (IBAction)test3:(id)sender {
WMOperation * wmOp = [[WMOperation alloc] init];
[wmOp start];
}
結(jié)果:
上面是最簡(jiǎn)單的自定義Operation,是一種串行操作,也是在主線程進(jìn)行的操作。為了更好的探究自定義類(lèi),我們?cè)傩陆▋蓚€(gè)類(lèi),一個(gè)是復(fù)雜一點(diǎn)的串行,一個(gè)是并行操作。
新建兩個(gè)類(lèi),如下圖:
WMCXOperation:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
自定義串行操作,NSOperation的子類(lèi)
*/
@interface WMCXOperation : NSOperation
@end
NS_ASSUME_NONNULL_END
#import "WMCXOperation.h"
@implementation WMCXOperation
- (void)main {
NSLog(@"main 開(kāi)始啦");
@try {
//任務(wù)是否結(jié)束標(biāo)識(shí)
BOOL isFinish = NO;
//只有當(dāng)沒(méi)有執(zhí)行完成和沒(méi)有被取消,才執(zhí)行自定義的相應(yīng)操作
while (isFinish==NO&&(self.isCancelled==NO)) {
//睡眠1秒,模擬耗時(shí)
sleep(1);
NSLog(@"線程:%@",[NSThread currentThread]);
isFinish = YES;
}
} @catch (NSException *exception) {
NSLog(@"出現(xiàn)異常:%@",exception);
} @finally {
NSLog(@"哈哈哈");
}
NSLog(@"main 結(jié)束啦");
}
@end
測(cè)試:
- (IBAction)test3:(id)sender {
WMOperation * wmOp = [[WMOperation alloc] init];
[wmOp start];
//自定義串行
WMCXOperation * cxOp = [[WMCXOperation alloc] init];
NSLog(@"任務(wù)開(kāi)始");
[cxOp start];
NSLog(@"任務(wù)結(jié)束");
//自定義并行
}
結(jié)果:
從結(jié)果可以看出,任務(wù)的執(zhí)行依然是在主線程,是串行操作。
自定義并行操作:
WMBXOperation
//
// WMBXOperation.h
// Multithreading
//
// Created by wenhuanhuan on 2020/10/14.
// Copyright ? 2020 weiman. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
自定義并行操作,NSOperation的子類(lèi)
自定義并行的 NSOperation 則要復(fù)雜一點(diǎn),首先必須重寫(xiě)以下幾個(gè)方法:
start: 所有并行的 Operations 都必須重寫(xiě)這個(gè)方法,然后在你想要執(zhí)行的線程中手動(dòng)調(diào)用這個(gè)方法。注意:任何時(shí)候都不能調(diào)用父類(lèi)的start方法。
main: 在start方法中調(diào)用,但是注意要定義獨(dú)立的自動(dòng)釋放池與別的線程區(qū)分開(kāi)。
isExecuting: 是否執(zhí)行中,需要實(shí)現(xiàn)KVO通知機(jī)制。
isFinished: 是否已完成,需要實(shí)現(xiàn)KVO通知機(jī)制。
isConcurrent: 該方法現(xiàn)在已經(jīng)由isAsynchronous方法代替,并且 NSOperationQueue 也已經(jīng)忽略這個(gè)方法的值。
isAsynchronous: 該方法默認(rèn)返回 NO ,表示非并發(fā)執(zhí)行。并發(fā)執(zhí)行需要自定義并且返回 YES。后面會(huì)根據(jù)這個(gè)返回值來(lái)決定是否并發(fā)。
與非并發(fā)操作不同的是,需要另外自定義一個(gè)方法來(lái)執(zhí)行操作而不是直接調(diào)用start方法.
*/
@interface WMBXOperation : NSOperation
//自定義方法,啟動(dòng)操作
-(BOOL)wmStart:(NSOperation *)op;
@end
NS_ASSUME_NONNULL_END
//
// WMBXOperation.m
// Multithreading
//
// Created by wenhuanhuan on 2020/10/14.
// Copyright ? 2020 weiman. All rights reserved.
//
#import "WMBXOperation.h"
@interface WMBXOperation()
{
BOOL executing;
BOOL finished;
}
@end
@implementation WMBXOperation
//重寫(xiě)init方法
-(instancetype)init{
if (self = [super init]) {
executing = NO;
finished = NO;
}
return self;
}
//重寫(xiě)start方法
-(void)start{
//如果任務(wù)被取消了
if (self.isCancelled) {
[self willChangeValueForKey:@"isFinished"];
finished = YES;
[self didChangeValueForKey:@"isFinished"];
return;
}
[self willChangeValueForKey:@"isExecuting"];
//使用NSThread開(kāi)辟子線程執(zhí)行main方法
[NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
executing = YES;
[self didChangeValueForKey:@"isExecuting"];
}
//重寫(xiě)main方法
-(void)main {
NSLog(@"main 開(kāi)始");
@try {
// 必須為自定義的 operation 提供 autorelease pool,因?yàn)?operation 完成后需要銷(xiāo)毀。
@autoreleasepool {
BOOL isfinish = NO;
while (isfinish==NO&&self.isCancelled==NO) {
NSLog(@"線程: %@", [NSThread currentThread]);
isfinish = YES;
}
[self completeOperation];
}
} @catch (NSException *exception) {
NSLog(@"異常:%@",exception);
} @finally {
NSLog(@"嘿嘿");
}
NSLog(@"main 結(jié)束");
}
- (void)completeOperation {
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
executing = NO;
finished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
}
-(BOOL)isAsynchronous{
return YES;
}
-(BOOL)isExecuting{
return executing;
}
-(BOOL)isFinished{
return finished;
}
-(BOOL)wmStart:(NSOperation *)op{
BOOL run = NO;
if ([op isReady]&&![op isCancelled]) {
if ([op isAsynchronous]==NO) {
[op start];
}else{
[NSThread detachNewThreadSelector:@selector(start) toTarget:op withObject:nil];
}
run = YES;
}else if ([op isCancelled]){
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
executing = NO;
finished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
run = YES;
}
return run;
}
@end
測(cè)試:
- (IBAction)test3:(id)sender {
WMOperation * wmOp = [[WMOperation alloc] init];
[wmOp start];
//自定義串行
WMCXOperation * cxOp = [[WMCXOperation alloc] init];
NSLog(@"任務(wù)開(kāi)始");
[cxOp start];
NSLog(@"任務(wù)結(jié)束");
//自定義并行
WMBXOperation * bxOp = [[WMBXOperation alloc] init];
NSLog(@"并行任務(wù)開(kāi)始");
[bxOp wmStart:bxOp];
NSLog(@"并行任務(wù)結(jié)束");
}
打印結(jié)果:
從結(jié)果可以看出,是在新的子線程中執(zhí)行的任務(wù)。
當(dāng)然了,因?yàn)槲覀冊(cè)僮远x類(lèi)的代碼中使用了NSthread的開(kāi)啟子線程的方法:
2.2 NSOperationQueue的使用
NSOperationQueue相當(dāng)于GCD的隊(duì)列。NSOperation有兩種隊(duì)列:
1.主隊(duì)列:mainQueue,在主隊(duì)列中的任務(wù)都在主線程執(zhí)行。
2.其他隊(duì)列:非主隊(duì)列通過(guò)設(shè)置最大并發(fā)數(shù)確定是串行還是并發(fā)隊(duì)列。
//主隊(duì)列
- (IBAction)mainQ:(id)sender {
NSOperationQueue * q1 = [NSOperationQueue mainQueue];
}
//非主隊(duì)列
- (IBAction)otherQ:(id)sender {
NSOperationQueue * q2 = [[NSOperationQueue alloc] init];
}
NSOperationQueue的作用
NSOperation的子類(lèi)對(duì)象是通過(guò)調(diào)用start方法來(lái)啟動(dòng)任務(wù)的。如果將對(duì)象添加到NSOperationQueue中,就不需要手動(dòng)啟動(dòng)了。
添加任務(wù)到隊(duì)列的兩個(gè)方法:
-(void)addOperation:(NSOperation *)op;
-(void)addOperationWithBlock:(void (^)(void))block;
下面,我們就把任務(wù)和隊(duì)列結(jié)合起來(lái),實(shí)現(xiàn)多線程。
2.3 NSOperation子類(lèi)和NSOperationQueue結(jié)合使用創(chuàng)建多線程
NSBlockOperation 不論封裝操作還是追加操作都是異步并發(fā)執(zhí)行
1》NSBlockOperation+主隊(duì)列
//block+主隊(duì)列
- (IBAction)blockAndMain:(id)sender {
//1.創(chuàng)建主隊(duì)列
NSOperationQueue * q1 = [NSOperationQueue mainQueue];
//2.創(chuàng)建任務(wù)
NSBlockOperation * p1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任務(wù)一,當(dāng)前線程:%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:2.0];
NSLog(@"任務(wù)一結(jié)束");
}];
[p1 addExecutionBlock:^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"任務(wù)二,線程:%@",[NSThread currentThread]);
}];
[p1 addExecutionBlock:^{
NSLog(@"任務(wù)三,線程:%@",[NSThread currentThread]);
}];
//3.把任務(wù)添加到隊(duì)列
[q1 addOperation:p1];
//也可以直接添加操作到隊(duì)列中
[q1 addOperationWithBlock:^{
NSLog(@"直接添加操作,%@",[NSThread currentThread]);
}];
}
結(jié)果:
從結(jié)果可以看出,三個(gè)操作是并發(fā)執(zhí)行的,雖然是在主隊(duì)列中添加任務(wù),但是任務(wù)并不是都在主線程執(zhí)行,而是有在主線程也有在子線程中并發(fā)執(zhí)行的。
2》NSBlockOperation+非主隊(duì)列
//block+非主隊(duì)列
- (IBAction)blockAndOther:(id)sender {
//創(chuàng)建非主隊(duì)列
NSOperationQueue * q1 = [[NSOperationQueue alloc] init];
NSBlockOperation * b1 = [[NSBlockOperation alloc] init];
[b1 addExecutionBlock:^{
NSLog(@"任務(wù)一,線程:%@",[NSThread currentThread]);
}];
[b1 addExecutionBlock:^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"任務(wù)二,線程:%@",[NSThread currentThread]);
}];
[b1 addExecutionBlock:^{
NSLog(@"任務(wù)三,線程:%@",[NSThread currentThread]);
}];
//把任務(wù)添加到隊(duì)列
[q1 addOperation:b1];
}
結(jié)果:
其實(shí),我們只使用NSBlockOperation也是可以實(shí)現(xiàn)多線程的,只是需要我們自己手動(dòng)開(kāi)啟任務(wù)。
//block+非主隊(duì)列
- (IBAction)blockAndOther:(id)sender {
//創(chuàng)建非主隊(duì)列
NSOperationQueue * q1 = [[NSOperationQueue alloc] init];
NSBlockOperation * b1 = [[NSBlockOperation alloc] init];
[b1 addExecutionBlock:^{
NSLog(@"任務(wù)一,線程:%@",[NSThread currentThread]);
}];
[b1 addExecutionBlock:^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"任務(wù)二,線程:%@",[NSThread currentThread]);
}];
[b1 addExecutionBlock:^{
NSLog(@"任務(wù)三,線程:%@",[NSThread currentThread]);
}];
//把任務(wù)添加到隊(duì)列
[q1 addOperation:b1];
//只使用NSBlockOperation
NSLog(@"-------只使用NSBlockOperation實(shí)現(xiàn)------------");
NSBlockOperation * b2 = [[NSBlockOperation alloc] init];
[b2 addExecutionBlock:^{
NSLog(@"1,線程:%@",[NSThread currentThread]);
}];
[b2 addExecutionBlock:^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"2,線程:%@",[NSThread currentThread]);
}];
[b2 addExecutionBlock:^{
NSLog(@"3,線程:%@",[NSThread currentThread]);
}];
[b2 start];
}
結(jié)果:
3》NSInvocationOperation+主隊(duì)列
- (IBAction)InvoAndMain:(id)sender {
NSOperationQueue * mainQ = [NSOperationQueue mainQueue];
NSInvocationOperation * p1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
NSInvocationOperation * p2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];
NSInvocationOperation * p3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task3) object:nil];
[mainQ addOperation:p1];
[mainQ addOperation:p2];
[mainQ addOperation:p3];
}
-(void)task1 {
NSLog(@"任務(wù)一, %@",[NSThread currentThread]);
}
-(void)task2 {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"任務(wù)二, %@",[NSThread currentThread]);
}
-(void)task3 {
NSLog(@"任務(wù)三, %@",[NSThread currentThread]);
}
結(jié)果:
任務(wù)在主隊(duì)列順序執(zhí)行,也就是串行。
4》NSInvocationOperation+非主隊(duì)列
- (IBAction)invoAndOther:(id)sender {
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
NSInvocationOperation * p1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
NSInvocationOperation * p2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];
NSInvocationOperation * p3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task3) object:nil];
[queue addOperation:p1];
[queue addOperation:p2];
[queue addOperation:p3];
}
-(void)task1 {
NSLog(@"任務(wù)一, %@",[NSThread currentThread]);
}
-(void)task2 {
[NSThread sleepForTimeInterval:2.0];
NSLog(@"任務(wù)二, %@",[NSThread currentThread]);
}
-(void)task3 {
NSLog(@"任務(wù)三, %@",[NSThread currentThread]);
}
結(jié)果:
由結(jié)果可以看出,三個(gè)任務(wù)并發(fā)執(zhí)行。
3. NSOperation其他重要用法
3.1 NSOperation的依賴(lài) - (void)addDependency:(NSOperation *)op;
有時(shí)候,我們需要給任務(wù)添加依賴(lài)關(guān)系,比如有兩個(gè)任務(wù)op1和op2,任務(wù)2必須要等待任務(wù)1執(zhí)行完成之后才能執(zhí)行,這個(gè)時(shí)候就可以添加任務(wù)2依賴(lài)于任務(wù)1 .
//任務(wù)依賴(lài)
- (IBAction)test1:(id)sender {
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
NSBlockOperation * op1 = [[NSBlockOperation alloc] init];
[op1 addExecutionBlock:^{
NSLog(@"任務(wù)一, %@",[NSThread currentThread]);
}];
[op1 addExecutionBlock:^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"任務(wù)二,%@",[NSThread currentThread]);
}];
NSBlockOperation * op2 = [[NSBlockOperation alloc] init];
[op2 addExecutionBlock:^{
NSLog(@"op2哦,%@",[NSThread currentThread]);
}];
//設(shè)置op2依賴(lài)于任務(wù)op1
//[op2 addDependency:op1];
[queue addOperation:op1];
[queue addOperation:op2];
}
我們?cè)谶@里有連個(gè)任務(wù),op1和op2,兩個(gè)任務(wù)都在非主隊(duì)列中執(zhí)行,我們先不添加依賴(lài),看看執(zhí)行結(jié)果:
從結(jié)果可以看出,任務(wù)一有兩個(gè)操作,任務(wù)二有一個(gè)操作,它們分別在不同的線程中執(zhí)行,不分先后。
添加依賴(lài)看看:
打印結(jié)果:
從結(jié)果可以看出,添加了依賴(lài)之后,任務(wù)二只會(huì)在任務(wù)一執(zhí)行完成之后才會(huì)執(zhí)行。
3.2 NSOperation執(zhí)行完成 completionBlock
如果想要在某個(gè)操作執(zhí)行完成之后在執(zhí)行某種操作,這個(gè)時(shí)候就可以使用completionBlock了。
//NSOperation執(zhí)行完成
- (IBAction)test2:(id)sender {
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任務(wù)一,%@",[NSThread currentThread]);
}];
[op1 addExecutionBlock:^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"任務(wù)二,%@",[NSThread currentThread]);
}];
[op1 addExecutionBlock:^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"任務(wù)三,%@",[NSThread currentThread]);
}];
[op1 addExecutionBlock:^{
NSLog(@"任務(wù)四,%@",[NSThread currentThread]);
}];
op1.completionBlock = ^{
NSLog(@"任務(wù)都執(zhí)行完成啦");
};
[queue addOperation:op1];
}
打印結(jié)果:
從結(jié)果可以看出,四個(gè)任務(wù)在四個(gè)子線程中完成,直到所有的任務(wù)都執(zhí)行完,才執(zhí)行了completionBlock內(nèi)的代碼。
3.3 最大并發(fā)數(shù) maxConcurrentOperationCount
我們可以設(shè)置隊(duì)列的最大并發(fā)數(shù)屬性,控制隊(duì)列是并發(fā)、串行還是不執(zhí)行。
maxConcurrentOperationCount>1: 并發(fā)
maxConcurrentOperationCount=1:串行
maxConcurrentOperationCount=-1:不限制,默認(rèn)值
maxConcurrentOperationCount=0:不會(huì)執(zhí)行任何操作
我們一起來(lái)試試。
- 串行
//maxConcurrentOperationCount 最大并發(fā)數(shù)
- (IBAction)test3:(id)sender {
//串行
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 1;
NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任務(wù)一,%@",[NSThread currentThread]);
}];
NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任務(wù)二,%@",[NSThread currentThread]);
}];
NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任務(wù)三,%@",[NSThread currentThread]);
}];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
}
打印結(jié)果:
只開(kāi)辟了一個(gè)線程,任務(wù)一個(gè)個(gè)的執(zhí)行。我們把任務(wù)二改成耗時(shí)操作,看看會(huì)不會(huì)阻塞當(dāng)前線程。
看看打印結(jié)果:
依然是順序執(zhí)行,說(shuō)明會(huì)阻塞當(dāng)前線程。
注意:如果我們是一個(gè)操作中的三個(gè)子任務(wù),我們?cè)O(shè)置隊(duì)列的最大并發(fā)數(shù)是沒(méi)有效果的,例如:
//2. 一個(gè)操作的多個(gè)任務(wù),設(shè)置最大并發(fā)數(shù)為1是沒(méi)有效果的
NSOperationQueue * queue2 = [[NSOperationQueue alloc] init];
queue2.maxConcurrentOperationCount = 1;
NSBlockOperation * op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1,%@",[NSThread currentThread]);
}];
[op4 addExecutionBlock:^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"2,%@",[NSThread currentThread]);
}];
[op4 addExecutionBlock:^{
NSLog(@"3,%@",[NSThread currentThread]);
}];
[queue2 addOperation:op4];
結(jié)果:
我們發(fā)現(xiàn),即使我們?cè)O(shè)置了隊(duì)列的最大并發(fā)數(shù)為1,由于我們只有一個(gè)操作,這個(gè)操作中有三個(gè)任務(wù),這三個(gè)任務(wù)還是在三個(gè)線程中執(zhí)行的,也不是順序的。由此可見(jiàn),最大操作數(shù)針對(duì)的是操作,也就是NSBlockOperation對(duì)象,而不是任務(wù)。
- 并行
我們?cè)O(shè)置最大并發(fā)數(shù)大于1,看看結(jié)果。
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 5;
NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任務(wù)一,%@",[NSThread currentThread]);
}];
NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"任務(wù)二,%@",[NSThread currentThread]);
}];
NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任務(wù)三,%@",[NSThread currentThread]);
}];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
打印結(jié)果:
我們發(fā)現(xiàn)三個(gè)任務(wù)是并發(fā)執(zhí)行的,符合我們的預(yù)期。
- 不執(zhí)行操作,設(shè)置為0
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 0;
NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任務(wù)一,%@",[NSThread currentThread]);
}];
NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"任務(wù)二,%@",[NSThread currentThread]);
}];
NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任務(wù)三,%@",[NSThread currentThread]);
}];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
當(dāng)我們?cè)O(shè)置了
queue.maxConcurrentOperationCount = 0;
的時(shí)候,發(fā)現(xiàn)不會(huì)有任何打印,操作都不再執(zhí)行。
3.4 隊(duì)列暫停 suspended
當(dāng)我們?cè)O(shè)置了suspended=YES之后,隊(duì)列就會(huì)暫停。
//隊(duì)列暫停,suspended
- (IBAction)test4:(id)sender {
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
queue.suspended = YES
NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任務(wù)一,%@",[NSThread currentThread]);
}];
[op1 addExecutionBlock:^{
NSLog(@"任務(wù)二開(kāi)始,%@",[NSThread currentThread]);
for (int i=0; i<10; i++) {
[NSThread sleepForTimeInterval:1.0];
NSLog(@"2: i=%d",i);
}
NSLog(@"任務(wù)二結(jié)束");
}];
[queue addOperation:op1];
}
這個(gè)時(shí)候,當(dāng)我們點(diǎn)擊按鈕,是不會(huì)有任何操作執(zhí)行的,因?yàn)殛?duì)列暫停了。
3.5 取消隊(duì)列的未執(zhí)行的所有操作
@interface OperationOtherController ()
@property(nonatomic, strong)NSOperationQueue * myQueue;
@end
//取消所有任務(wù)
- (IBAction)test5:(id)sender {
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"操作一:1,%@",[NSThread currentThread]);
}];
[op1 addExecutionBlock:^{
NSLog(@"操作一:2,%@",[NSThread currentThread]);
}];
NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"操作二:1,%@",[NSThread currentThread]);
}];
NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"操作三:1,%@",[NSThread currentThread]);
}];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
self.myQueue = queue;
}
- (IBAction)cancelTest5:(id)sender {
[self.myQueue cancelAllOperations];
}
打印結(jié)果:
我們發(fā)現(xiàn),即使我們點(diǎn)擊了取消按鈕,未執(zhí)行的操作三并沒(méi)有被取消。
注意:暫停和取消只能暫停或取消處于等待狀態(tài)的任務(wù),不能暫停或取消正在執(zhí)行中的任務(wù),必須等正在執(zhí)行的任務(wù)執(zhí)行完畢之后才會(huì)暫停,如果想要暫停或者取消正在執(zhí)行的任務(wù),可以在每個(gè)任務(wù)之間即每當(dāng)執(zhí)行完一段耗時(shí)操作之后,判斷是否任務(wù)是否被取消或者暫停。如果想要精確的控制,則需要將判斷代碼放在任務(wù)之中,但是不建議這么做,頻繁的判斷會(huì)消耗太多時(shí)間
3.6 打印隊(duì)列中所有的操作對(duì)象
- (IBAction)test6:(id)sender {
NSLog(@"打印所有操作");
NSLog(@"%@",self.myQueue.operations);
}
結(jié)果:
- 案例:下載圖片并合成
//
// OperationCaseController.m
// Multithreading
//
// Created by wenhuanhuan on 2020/10/16.
// Copyright ? 2020 weiman. All rights reserved.
//
#import "OperationCaseController.h"
@interface OperationCaseController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageV1;
@property (weak, nonatomic) IBOutlet UIImageView *imageV2;
@property (weak, nonatomic) IBOutlet UIImageView *imageVFinal;
@property(nonatomic,strong)UIImage * image1;
@property(nonatomic,strong)UIImage * image2;
@end
@implementation OperationCaseController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction)startAction:(id)sender {
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
//下載圖片一
NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
NSString * str = @"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1601638749466&di=92ace2ffa924fe6063e7a221729006b1&imgtype=0&src=http%3A%2F%2Fpic.autov.com.cn%2Fimages%2Fcms%2F20119%2F6%2F1315280805177.jpg";
UIImage * image = [self downLoadImage:str];
self.image1 = image;
//回到主線程,顯示圖片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageV1.image = image;
}];
}];
//下載圖片二
NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
NSString * str = @"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1601638873771&di=07129fd95c56096a4282d3b072594491&imgtype=0&src=http%3A%2F%2Fimg.51miz.com%2Fpreview%2Felement%2F00%2F01%2F12%2F49%2FE-1124994-5FFE5AC7.jpg";
UIImage * image = [self downLoadImage:str];
self.image2 = image;
//回到主線程,顯示圖片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageV2.image = image;
}];
}];
//合成圖片
NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
UIImage * image = [self makeImage:self.image1 image2:self.image2];
//回到主線程,顯示圖片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageVFinal.image = image;
}];
}];
//由于合成圖片要在圖片一和圖片二完成之后才能進(jìn)行,所以需要添加依賴(lài)
[op3 addDependency:op1];
[op3 addDependency:op2];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
}
-(UIImage *)downLoadImage:(NSString *)str {
NSLog(@"當(dāng)前線程:%@",[NSThread currentThread]);
NSURL * url = [NSURL URLWithString:str];
NSData * data = [NSData dataWithContentsOfURL:url];
UIImage * image = [UIImage imageWithData:data];
return image;
}
-(UIImage *)makeImage:(UIImage *)image1 image2:(UIImage *)image2 {
//圖形上下文開(kāi)啟
UIGraphicsBeginImageContext(CGSizeMake(300, 200));
//圖形二
[image2 drawInRect:CGRectMake(0, 0, 300, 200)];
//圖形一
[image1 drawInRect:CGRectMake(100, 50, 100, 100)];
//獲取新的圖片
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
//關(guān)閉上下文
UIGraphicsEndImageContext();
return image;
}
@end
運(yùn)行結(jié)果:
以上就是關(guān)于NSOperation創(chuàng)建多線程的探究?jī)?nèi)容了,如有錯(cuò)漏還請(qǐng)指教。
祝大家生活愉快。