配合使用NSOperation 和 NSOperationQueue 实现多线程编程。
NSOperation实现多线程的步骤
1, 将要执行的操作封装到NSOperation对象中
2, 将NSOperation 对象添加到 NSOperationQueue中
3, 系统自动将NSOperationQueue中的NSOperation 取出来
4, 将NSOperation 封装的操作放到一条新线程执行
NSOperation的子类
NSOperation是个抽象类,并不具有封装操作的能力。使用方式有3种
1,NSInvocationOperation
2,NSBlockOperation
3,自定义子类继承NSOperation ,实现内部相应的方法
NSOperationQueue 队列组
1 ,默认就是并发执行的啦
2, 那么我们怎么设置串行执行呢 。队列组有个属性 maxConcurrentOperationCount 设置最大并发数 ,就可以啦,这样就是串行执行的了
maxConcurrentOperationCount = 1 ;串行队列
maxConcurrentOperationCount > 1 ; 并发队列
maxConcurrentOperationCount = 0 ; 不会执行任务
maxConcurrentOperationCount = -1 ; 特殊意义。表示的是最大值。
默认并发数就是 static const NSInteger NSOperationQueueDefaultMaxConcurrentOperationCount = -1;
/*
暂停可恢复,取消不可恢复
队列中的任务也是有状态的:已经执行完毕 | 正在执行|排队等待 ,不管是暂停还是取消都会等待正在执行的任务结束才会执行。
*/
//暂停 ,不能暂停当前正在执行的任务
[queue setSuspended:YES];
// 继续
[queue setSuspended:NO];
/*取消,不可恢复 。取消队列里面的所有任务,我们能拿到 cancelled 属性,
这样就可以判断队列是否取消任务,可以在 队列任务里面进行判断,
用于即时取消任务等,因为正在执行的任务不会里面执行,
所有cancelled 。可以帮助判断*/
[queue cancelAllOperations];
NSOperationQueue 的一个只读属性 cancelled 。返回BOOL,队列是否取消了任务
3,注意:串行执行 != 只开了一条线程
方式一 :NSInvocationOperation 简单的封装任务,封装操作,不和NSOperationQueue配合使用的时候,没有任何作用,并不会开启子线程。
-(void)invocationOperation{
//1,封装操作,封装任务
/*
1.参数1:目标对象 self
2.参数2:调用的方法
3.参数3:方法的参数
*/
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test1) object:nil];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test2) object:nil];
/*
[NSOperationQueue mainQueue]; 获取主队列
NSOperation队列:
1,主队列 : [NSOperationQueue mainQueue] 和gcd主队列一样,任务都在主线程执行,主队列是串行队列
2,非主队列:[[NSOperationQueue alloc] init] 特殊(同时具备并发和串行的功能),默认情况下非主队列是并发队列
*/
//2 ,创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//设置最大并发数的数量,同一时间最多多少任务可以执行 ,设置为1 就是串行啦
queue.maxConcurrentOperationCount = 2;
//3 , 添加操作到队列中 ,
[queue addOperation:op1]; //内部已经调用了 [op1 start]; ,调用了start后又调用了内部的main方法 。我们可以用继承重写start,和main 方法。把任务封装到 main方法里面
[queue addOperation:op2];
[queue addOperation:op3];
//,启动操作 ,如果不 和NSOperationQueue 配合操作。就必须手动启动一下,
//并且NSInvocationOperation,单独使用并不会开启多线程
//[op1 start];
}
-(void)test
{
NSLog(@"%s __func__ NSThread = %@" ,__func__,[NSThread currentThread]);
}
-(void)test1
{
NSLog(@"%s __func__ NSThread = %@" ,__func__,[NSThread currentThread]);
}
-(void)test2
{
NSLog(@"%s __func__ NSThread = %@" ,__func__,[NSThread currentThread]);
}
/* 打印 ,看打印,不难理解,内部是并发执行
2017-11-06 23:08:11.404 NSOperationDemo[1847:56183] -[ViewController test] __func__ NSThread = <NSThread: 0x6080000776c0>{number = 4, name = (null)}
2017-11-06 23:08:11.404 NSOperationDemo[1847:56165] -[ViewController test2] __func__ NSThread = <NSThread: 0x60800007b140>{number = 5, name = (null)}
2017-11-06 23:08:11.404 NSOperationDemo[1847:56166] -[ViewController test1] __func__ NSThread = <NSThread: 0x60000007b9c0>{number = 3, name = (null)}
*/
方式二 :NSBlockOperation 单独使用不配合 queue 队列, 不会开启子线程,但是一个操作中任务数量大于1 ,会开启子线程,详情如下
-(void)blockOperation
{
//1,创建任务
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1 -- %@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2 -- %@",[NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3 -- %@",[NSThread currentThread]);
}];
//2 ,追加任务
//注意: 如果一个操作中任务数量大于1 , 会开子线程并发执行任务,
并不一定是子线程也许是主线程,比如op3 就有4个任务,所以会开子线程,
并发执行任务。而 op1 ,op2,不会开子线程在主线程执行
[op3 addExecutionBlock:^{
NSLog(@"4 -- %@",[NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"5 -- %@",[NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"6 -- %@",[NSThread currentThread]);
}];
//3 ,启动
[op1 start];
[op2 start];
[op3 start];
}
/* 打印结果
2017-11-06 22:38:20.505 NSOperationDemo[1399:33218] 1 -- <NSThread: 0x60800006ba80>{number = 1, name = main}
2017-11-06 22:38:20.505 NSOperationDemo[1399:33218] 2 -- <NSThread: 0x60800006ba80>{number = 1, name = main}
2017-11-06 22:38:20.506 NSOperationDemo[1399:33218] 3 -- <NSThread: 0x60800006ba80>{number = 1, name = main}
2017-11-06 22:38:20.506 NSOperationDemo[1399:33218] 5 -- <NSThread: 0x60800006ba80>{number = 1, name = main}
2017-11-06 22:38:20.506 NSOperationDemo[1399:33259] 4 -- <NSThread: 0x600000076740>{number = 3, name = (null)}
2017-11-06 22:38:20.506 NSOperationDemo[1399:33261] 6 -- <NSThread: 0x6000000766c0>{number = 4, name = (null)}
*/
方式二:NSBlockOperation 单独使用配合 queue 队列,开启多线程
#pragma mark NSBlockOperation 配合队列 queue 的 使用
-(void)blockOperationWithQueue
{
//1,创建任务
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1 -- %@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2 -- %@",[NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3 -- %@",[NSThread currentThread]);
}];
//2 ,追加任务
//注意: 如果一个操作中任务数量大于1 , 会开子线程并发执行任务,并不一定是子线程也许是主线程,比如op3 就有4个任务,所以会开子线程,并发执行任务
[op3 addExecutionBlock:^{
NSLog(@"4 -- %@",[NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"5 -- %@",[NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"6 -- %@",[NSThread currentThread]);
}];
//2 ,创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//3 ,启动
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
//简便方法 ,相当于,创建了任务,并添加到了 queue
[queue addOperationWithBlock:^{
NSLog(@"7 -- %@",[NSThread currentThread]);
}];
}
/* 打印如下
2017-11-06 23:17:08.596 NSOperationDemo[1886:61154] 7 -- <NSThread: 0x600000263940>{number = 7, name = (null)}
2017-11-06 23:17:08.596 NSOperationDemo[1886:61099] 1 -- <NSThread: 0x60000007f140>{number = 6, name = (null)}
2017-11-06 23:17:08.598 NSOperationDemo[1886:61155] 2 -- <NSThread: 0x6000002603c0>{number = 8, name = (null)}
2017-11-06 23:17:08.598 NSOperationDemo[1886:61154] 3 -- <NSThread: 0x600000263940>{number = 7, name = (null)}
2017-11-06 23:17:08.598 NSOperationDemo[1886:61113] 4 -- <NSThread: 0x60800007be40>{number = 4, name = (null)}
2017-11-06 23:17:08.598 NSOperationDemo[1886:61099] 5 -- <NSThread: 0x60000007f140>{number = 6, name = (null)}
2017-11-06 23:17:08.599 NSOperationDemo[1886:61155] 6 -- <NSThread: 0x6000002603c0>{number = 8, name = (null)
*/
方式三:自定义子类继承NSOperation ,实现内部相应的方法
好处:1,利于代码隐蔽
2, 复用性,耦合性
#import <Foundation/Foundation.h>
//XCOperation 继承子类实现
@interface XCOperation : NSOperation
@end
#import "XCOperation.h"
@implementation XCOperation
//告诉要执行的任务是什么
-(void)main{
NSLog(@"__Func__ %s , NSThread = %@",__func__,[NSThread currentThread]);
}
#pragma mark 继承 NSOperation 使用方法
-(void)customOperation
{
//1 ,封装操作
XCOperation *op1 = [[XCOperation alloc] init];
XCOperation *op2 = [[XCOperation alloc] init];
//2 ,创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//3 ,添加 启动
[queue addOperation:op1];
[queue addOperation:op2];
}
/* 打印
NSOperationDemo[1950:69064] __Func__ -[XCOperation main] , NSThread = <NSThread: 0x600000074880>{number = 3, name = (null)}
NSOperationDemo[1950:69044] __Func__ -[XCOperation main] , NSThread = <NSThread: 0x600000075680>{number = 4, name = (null)}
*/
NSBlockOperation 操作依赖,控制任务顺序 ,监听任务完成情况。
#pragma mark NSBlockOperation 操作依赖 ,控制任务顺序 ,于操作监听,监听某个任务完成状态
-(void)blockOperationWithQueueRelyon
{
//1,创建任务
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1 -- %@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2 -- %@",[NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 8; i ++) {
NSLog(@"3 -- %@",[NSThread currentThread]);
if (i == 7) {
NSLog(@"任务3 ,打印到了7 跳出循环任务结束");
break;
}
}
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"4 -- %@",[NSThread currentThread]);
}];
//2 ,创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
//3.操作依赖
//注意:不能循环依赖,比如操作1 依赖 操作3 。操作3 又来依赖操作1 。会互相等待
//这里是操作1依赖操作3 。操作3 依赖操作2. 所有打印顺序是 2 -> 3 -> 1 -> 4
//可以跨队列依赖
[op1 addDependency:op3]; // 任务1 依赖任务3
[op3 addDependency:op2]; // 任务3 依赖任务2
[op4 addDependency:op1]; // 任务4 依赖任务1 所以 打印顺序是 2 -> 3 -> 1 -> 4
//4.添加 到队列组
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue1 addOperation:op4];
//5,操作监听,监听任务3 ,任务3 完成了,打印这里
op3.completionBlock = ^{
NSLog(@"监听到任务3结束了 ,虽然监听到任务3结束,但是他和任务3并不一定在一个线程 ,%@",[NSThread currentThread]);
};
}
/* 可以看出顺序是2 3 1 4 ,监听到了任务3
2017-11-07 22:52:18.344 NSOperationDemo[1045:29786] 2 -- <NSThread: 0x60800007b7c0>{number = 7, name = (null)}
2017-11-07 22:52:18.346 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.346 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.347 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.347 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.348 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.348 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.350 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.350 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.350 NSOperationDemo[1045:30254] 任务3 ,打印到了90 跳出循环任务结束
2017-11-07 22:52:18.351 NSOperationDemo[1045:29786] 监听到任务3结束了 ,虽然监听到任务3结束,但是他和任务3并不一定在一个线程 ,<NSThread: 0x60800007b7c0>{number = 7, name = (null)}
2017-11-07 22:52:18.351 NSOperationDemo[1045:30254] 1 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.354 NSOperationDemo[1045:29786] 4 -- <NSThread: 0x60800007b7c0>{number = 7, name = (null)}
*/
线程之间的通信 。例如: 子线程下载图片,下载完成通知主线程刷新
#pragma makr 线程中的通信 ,例子子线程下载图片,主线程刷新
-(void)downloadImage
{
//1 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2 封装任务
NSBlockOperation *download = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"http://static.firefoxchina.cn/img/201710/4_59e999c8e6aa50.jpg"];
NSData *imageData = [NSData dataWithContentsOfURL:url];
NSLog(@"子线程下载图片%@" ,[NSThread currentThread]);
UIImage *image = [UIImage imageWithData:imageData];
//4,线程通信,回到主线程,刷新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageview.image = image ;
}];
// [queue addOperationWithBlock:^{
// NSLog(@"7 -- %@",[NSThread currentThread]);
// }];
// [queue addOperationWithBlock:^{
// NSLog(@"8 -- %@",[NSThread currentThread]);
// }];
// [queue addOperationWithBlock:^{
// NSLog(@"9 -- %@",[NSThread currentThread]);
// }];
}];
//3 添加到queue
[queue addOperation:download];
}
学习记录,简单的敲一下增强记忆,便于查找,😄