okay,多线程中最后一块小内容——NSOperation
- <h5>NSOperation的作用</h5>
- 配合使用NSOperation和NSOperationQueue也能实现多线程编码
- <h5>NSOperation和NSOperationQueue实现多线程的具体步骤</h5>
- 先将需要执行的操作封装到一个NSOperation对象中
- 然后将NSOperation对象添加到NSOperationQueue中
- 系统会自动将NSOperationQueue中的NSOperation取出来
- 将取出的NSOperation封装的操作放到一条新线程中执行
- <h5>NSOperation的子类</h5>
- NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
- 使用NSOperation子类的方式有3种
- NSInvocationOperation
- NSBlockOperation
- 自定义子类,继承NSOperation,实现内部相应的方法
- <h5>NSInvocationOperation</h5>
- (void)invocationOperation {
- 01.创建操作,封装任务
/**
第一个参数:目标对象 self
第二个参数:(方法选择器)调用方法的名称
第三个参数:前面方法需要接受的参数
*/
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download2) object:nil];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download3) object:nil];
- 02.启动|执行操作
[op1 start];
[op2 start];
[op3 start];
}
- (void)download1 {
NSLog(@"%@", [NSThread currentThread]); // 主线程执行
}
- (void)download2 {
NSLog(@"%@", [NSThread currentThread]); // 主线程执行
}
- (void)download3 {
NSLog(@"%@", [NSThread currentThread]); // 主线程执行
}
- <h5>NSBlockOperation</h5>
- (void)blockOperation {
- 01.创建操作
NSBlockOperation *bop1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1------------%@", [NSThread currentThread]); // 主线程执行
}];
NSBlockOperation *bop2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2------------%@", [NSThread currentThread]); // 主线程执行
}];
NSBlockOperation *bop3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3------------%@", [NSThread currentThread]); // 主线程执行
}];
- 追加任务
// !!注意:如果一个操作中的任务数量大于1,那么会开子线程并发执行任务
// !!注意:不一定非要开子线程,有可能是主线程
[bop3 addExecutionBlock:^{
NSLog(@"++++++++ 4 +++++++++%@", [NSThread currentThread]); // 有可能在子线程执行
}];
[bop3 addExecutionBlock:^{
NSLog(@"++++++++ 5 +++++++++%@", [NSThread currentThread]); // 有可能在子线程执行
}];
[bop3 addExecutionBlock:^{
NSLog(@"++++++++ 6 +++++++++%@", [NSThread currentThread]); // 有可能在子线程执行
}];
- 02.启动|执行操作
[bop1 start];
[bop2 start];
[bop3 start];
}
- <h5>NSOperationQueue</h5>
- NSOperationQueue的作用
- NSOperation可以调用start方法来执行任务,但默认是同步执行
- 如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
- NSOperationQueue的作用
- (void)invocationOperationWithQueue {
- 01.创建操作,封装任务
/**
第一个参数:目标对象 self
第二个参数:(方法选择器)调用方法的名称
第三个参数:前面方法需要接受的参数
*/
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download01) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download02) object:nil];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download03) object:nil];
- 02.创建队列
/**
GCD:
串行队列:create & 主队列
并发队列:create & 全局并发队列
NSOperation:
主队列:[NSOperationQueue mainQueue] 和GCD中的主队列一样,串行队列
非主队列:[[NSOperationQueue alloc] init] 非常特殊(同时具备并发和串行的功能)
// 默认情况下,非主队列是并发队列
*/
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
- 03.添加操作到队列中执行
[queue addOperation:op1]; //内部已经待用了start方法
[queue addOperation:op2];
[queue addOperation:op3];
}
- (void)download01 {
NSLog(@"%@", [NSThread currentThread]); // 子线程执行
}
- (void)download02 {
NSLog(@"%@", [NSThread currentThread]); // 子线程执行
}
- (void)download03 {
NSLog(@"%@", [NSThread currentThread]); // 子线程执行
}
- (void)blockOperationWithQueue {
- 01.创建操作
NSBlockOperation *bop1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1-------------%@", [NSThread currentThread]); // 子线程执行任务(并发执行)
}];
NSBlockOperation *bop2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2-------------%@", [NSThread currentThread]); // 子线程执行任务(并发执行)
}];
NSBlockOperation *bop3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3-------------%@", [NSThread currentThread]); // 子线程执行任务(并发执行)
}];
- 追加任务
[bop2 addExecutionBlock:^{
NSLog(@"+++++++++ 4 +++++++++%@", [NSThread currentThread]); // 子线程执行任务(并发执行)
}];
[bop2 addExecutionBlock:^{
NSLog(@"+++++++++ 5 +++++++++%@", [NSThread currentThread]); // 子线程执行任务(并发执行)
}];
[bop2 addExecutionBlock:^{
NSLog(@"+++++++++ 6 +++++++++%@", [NSThread currentThread]); // 子线程执行任务(并发执行)
}];
- 02.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
- 03.把操作添加到队列里面
[queue addOperation:bop1];
[queue addOperation:bop2];
[queue addOperation:bop3];
- 简单方法 添加任务开子线程
[queue addOperationWithBlock:^{
NSLog(@"////////////// 7 ////////////// %@", [NSThread currentThread]); // 子线程并发执行
}];
}
// 自定义VtcOperation分类,继承自NSOperation
// .m中,重写main方法,告知要执行的任务是什么,
// 1.有利于代码隐蔽
// 2.复用性,
// 一般将复杂代码写在下面(重写main)方法中,外界不需要关心内部任务的执行过程和内容,
- (void)main {
NSLog(@"main-------%@", [NSThread currentThread]);
}
// 外界调用
- (void)customOperationWithQueue {
VtcOperation *op1 = [[VtcOperation alloc] init];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
}
- <h5>NSOperation的其他用法</h5>
@property (nonatomic, strong) NSOperationQueue *queue;
- <h6>开始任务</h6>
- (IBAction)startOperationBtnClick:(id)sender {
- 1.创建队列
// 默认是并发队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
- 2.设置
最大并发数
: maxConcurrrentOperationCount
// 同一时间组多有多少个任务可以执行
// 串行执行任务 != 只开一条线程(线程同步)
// maxConcurrentOperationCount > 1 那么就是并发队列
// maxConcurrentOperationCount == 1 那么就是串行队列
// maxConcurrentOperationCount == 0 不会执行任务
// maxConcurrentOperationCount == -1 特殊意义 最大值 表示不受限制
queue.maxConcurrentOperationCount = 1;
- 3.封装操作
NSBlockOperation *bop1 = [NSBlockOperation blockOperationWithBlock:^{
for (NSInteger i = 0; i < 10000; i++) {
NSLog(@"---1---%zd------%@", i, [NSThread currentThread]);
}
}];
NSBlockOperation *bop2 = [NSBlockOperation blockOperationWithBlock:^{
for (NSInteger i = 0; i < 10000; i++) {
NSLog(@"---2---%zd------%@", i, [NSThread currentThread]);
}
}];
NSBlockOperation *bop3 = [NSBlockOperation blockOperationWithBlock:^{
for (NSInteger i = 0; i < 10000; i++) {
NSLog(@"---3---%zd------%@", i, [NSThread currentThread]);
}
}];
NSBlockOperation *bop4 = [NSBlockOperation blockOperationWithBlock:^{
for (NSInteger i = 0; i < 10000; i++) {
NSLog(@"---4---%zd------%@", i, [NSThread currentThread]);
}
}];
[self.queue addOperation:bop1];
[self.queue addOperation:bop2];
[self.queue addOperation:bop3];
[self.queue addOperation:bop4];
}
- <h6>暂停任务</h6>
- (IBAction)suspendOperationBtnClick:(id)sender {
// 是可以恢复的
/**
队列中的任务也是有状态的:已经执行完毕的|正在执行|排队等待状态
*/
// 不能暂停当前正在处于执行状态的任务(如上代码,只有block中for循环结束后才可暂停)
[self.queue setSuspended:YES];
}
- <h6>继续任务</h6>
- (IBAction)proceedOperationBtnClick:(id)sender {
[self.queue setSuspended:NO];
}
- <h6>结束任务</h6>
- (IBAction)cancelOperationBtnClick:(id)sender {
// 不可以恢复
[self.queue cancelAllOperations];
}
<b>!!· 如果自定义NSOperation,执行如上代码会出现一些问题</b>
// 创建VtcOperation分类,继承NSOperation
// 重写main方法
- (void)main {
// 模拟三个耗时操作
for (NSInteger i = 0; i < 10000; i++) {
NSLog(@"---download--1------%zd----------%@", i, [NSThread currentThread]);
}
- <b>苹果官方建议</b>
if (self.isCancelled) return; // 一定要加这个,才能执行取消任务的操作
NSLog(@"+++++++++ main +++++++++");
for (NSInteger i = 0; i < 10000; i++) {
NSLog(@"---download--2------%zd----------%@", i, [NSThread currentThread]);
}
if (self.isCancelled) return; // 一定要加这个,才能执行取消任务的操作
NSLog(@"+++++++++ main +++++++++");
for (NSInteger i = 0; i < 10000; i++) {
NSLog(@"---download--3------%zd----------%@", i, [NSThread currentThread]);
}
NSLog(@"+++++++++ main +++++++++");
}
- 外界调用
@property (nonatomic, strong) NSOperationQueue *queue;
- (IBAction)startOperationBtnClick:(id)sender {
self.queue = [[NSOperationQueue alloc] init];
self.queue.maxConcurrentOperationCount = 1;
VtcOperation *bop1 = [[VtcOperation alloc] init];
[self.queue addOperation:bop1];
}
- 暂停任务
- (IBAction)suspendOperationBtnClick:(id)sender {
// 是可以恢复的
/**
队列中的任务也是有状态的:已经执行完毕的|正在执行|排队等待状态
*/
// 不能暂停当前正在处于执行状态的任务
[self.queue setSuspended:YES];
}
- 继续任务
- (IBAction)proceedOperationBtnClick:(id)sender {
[self.queue setSuspended:NO];
}
- 结束任务
- (IBAction)cancelOperationBtnClick:(id)sender {
// 不可以恢复
[self.queue cancelAllOperations];
}
- <h6>NSOperation操作依赖和操作监听</h6>
- (void)dependencyAndCompletion {
- 01.创建队列
NSOperationQueue *queueA = [[NSOperationQueue alloc] init];
NSOperationQueue *queueB = [[NSOperationQueue alloc] init];
- 02.封装操作
NSBlockOperation *bop1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"---1-----------%@", [NSThread currentThread]);
}];
NSBlockOperation *bop2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"---2-----------%@", [NSThread currentThread]);
}];
NSBlockOperation *bop3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"---3-----------%@", [NSThread currentThread]);
}];
NSBlockOperation *bop4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"---4-----------%@", [NSThread currentThread]);
}];
// 需求1:bop4完成之后再执行bop1
// 添加操作依赖
// 注意点:不能循环依赖,如:bop1依赖bop4,bop4依赖bop1 (两个都不会执行)
// 可以跨队列依赖
[bop1 addDependency:bop4];
[bop2 addDependency:bop3];
// 需求2:当bop3完成之后,发送消息
// 操作监听
bop3.completionBlock = ^{
NSLog(@"---bop3任务已完成--------------%@", [NSThread currentThread]);
};
- 03.添加操作到队列
[queueA addOperation:bop1];
[queueA addOperation:bop2];
[queueB addOperation:bop3];
[queueB addOperation:bop4];
}
- <h6>NSOperation实现线程间通信</h6>
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
- (void) downloadImage {
- 01.开子线程下载图片
// 1.1 非主队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 1.2 封装操作
NSBlockOperation *download = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@""];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
NSLog(@"---download-----------%@", [NSThread currentThread]);
- 03 刷新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
NSLog(@"---UI-------------%@", [NSThread currentThread]);
}];
}];
- 02.添加操作到队列
[queue addOperation:download];
}
- <h6>NSOperation线程间通信应用</h6>
- 需求:点击控制器View,开子线程下载两张图片,将两张图片合并成一张(刷新UI,展现出来)
- (void)compoundImages {
__block UIImage *image1;
__block UIImage *image2;
- 01 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
- 02 封装操作1 下载图片1
NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"http://file.cbda.cn/uploadfile/2015/0330/20150330041852447.jpg"];
NSData *imageData = [NSData dataWithContentsOfURL:url];
image1 = [UIImage imageWithData:imageData];
}];
- 03 封装操作2 下载图片2
NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"http://www.33lc.com/soft/UploadPic/2012-8/20128179452663218.jpg"];
NSData *imageData = [NSData dataWithContentsOfURL:url];
image2 = [UIImage imageWithData:imageData];
}];
- 04 封装合并图片操作
NSBlockOperation *compoundImages = [NSBlockOperation blockOperationWithBlock:^{
// 4.1 开启上下文
UIGraphicsBeginImageContext(CGSizeMake(300, 300));
// 4.2 画图1
[image1 drawInRect:CGRectMake(0, 0, 300, 150)];
// 4.3 画图2
[image2 drawInRect:CGRectMake(0, 150, 300, 150)];
// 4.4 根据图片上下文得到合并后的图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 4.5 关闭图片上下文
UIGraphicsEndImageContext();
NSLog(@"---draw----------%@", [NSThread currentThread]); //子线程完成画图
- 07 回到主线程刷新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
NSLog(@"---UI------------%@", [NSThread currentThread]); // 主线程刷新UI
}];
}];
- 05 设置依赖关系
[compoundImages addDependency:download1];
[compoundImages addDependency:download2];
- 06 添加操作到队列里面
[queue addOperation:download1];
[queue addOperation:download2];
[queue addOperation:compoundImages];
}