1. NSOperation 的子类实现3种方式: 在UI线程中执行都是
1. NSInvocationOperation
2. NSBlockOperation
3. 自定义子类实现
1. NSInvocationOperation: 通过代理实
-(void)touchesBegan1:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
/**
1. 第一个参数 : 目标对象
第二个参数: 选择器,要调用的方法
第三方参数: 方法要传递的参数
*/
NSInvocationOperation* op = [[ NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
// 启动操作
[op start];
}
-(void)download{
NSLog(@"%s-----%@---",__func__, [NSThread currentThread]);
}
2. NSBlockOperation: 通过代码块实现
-(void)touchesBegan1:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
/** 2.NSBlockOperation
*
*/
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
// 在主线程中执行
// {number = 1, name = main}
NSLog(@"2------%@",[NSThread currentThread]);
}];
//2.追加操作
//在子线程中执行
[op1 addExecutionBlock:^{
//{number = 3, name = (null)}
NSLog(@"3------%@",[NSThread currentThread]);
}];
[op1 addExecutionBlock:^{
// {number = 8, name = (null)}
NSLog(@"4------%@",[NSThread currentThread]);
}];
[op1 addExecutionBlock:^{
// {number = 9, name = (null)}
NSLog(@"5------%@",[NSThread currentThread]);
}];
//3.启动操作执行
[op1 start];
}
3. 自定义子类实现 : 通过重写 main 函数实现
XMGOperation.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface XMGOperation : NSOperation
@end
NS_ASSUME_NONNULL_END
==============================================
XMGOperation.m
#import "XMGOperation.h"
@implementation XMGOperation
// 问题: 如果使用 1. NSInvocationOperation 2. NSBlockOperation 和2个子类
// 代码 直接 卸载 block中,会非常冗余
// 这里 单独写在 一个类中
// 1. 高封装, 信息隐蔽
// 2. 复用性
- (void)main{
// ....
// 一万 行代码 .....
// ----XMGOperation----<NSThread: 0x600002a9d9c0>{number = 1, name = main}
NSLog(@"----XMGOperation----%@",[NSThread currentThread]);
}
@end
调用:
-(void)touchesBegan1:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
XMGOperation* op2= [[XMGOperation alloc] init];
[op2 start];
}
2. NSOperationQueue队列 + NSOperation子类: 在子线程中执行都是,并发队列
1. NSOperationQueue队列 + NSInvocationOperation
2. NSOperationQueue队列 + NSBlockOperation
3. NSOperationQueue队列 + 自定义子类实现
1. NSOperationQueue队列 + NSInvocationOperation
-(void)touchesBegan2:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// NSOperationQueue 队列
// 主队列: 凡是放在主队列中的任务都是在主线程中执行的 [NSOperationQueue mainQueue]
// 非主要队列: 同时具备并发和串行的功能,默认是并发队列 [[NSOperationQueue alloc] init]]
/**
并发,在子线程中执行
-[ViewController1 download3]--3---<NSThread: 0x600002aef440>{number = 6, name = (null)}---
-[ViewController1 download2]--2---<NSThread: 0x600002ad0bc0>{number = 4, name = (null)}---
-[ViewController1 download1]---1--<NSThread: 0x600002ae1180>{number = 7, name = (null)}---
*/
// 1. 创建队列
NSOperationQueue* queue = [[NSOperationQueue alloc ]init];
// 2. 创建任务
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];
// 3. 把任务加入队列
[queue addOperation:op1];
// 添加以后会自动调用 start 方法
[queue addOperation:op2];
[queue addOperation:op3];
}
-(void)download1{
NSLog(@"%s---1--%@---",__func__, [NSThread currentThread]);
}
-(void)download2{
NSLog(@"%s--2---%@---",__func__, [NSThread currentThread]);
}
-(void)download3{
NSLog(@"%s--3---%@---",__func__, [NSThread currentThread]);
}
2. NSOperationQueue队列 + NSBlockOperation
-(void)touchesBegan3:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// 1. 创建队列
NSOperationQueue* queue = [[NSOperationQueue alloc ]init];
// 2. 封装操作
NSBlockOperation* op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%s---1--%@---",__func__, [NSThread currentThread]);
}];
NSBlockOperation* op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%s---2--%@---",__func__, [NSThread currentThread]);
}];
NSBlockOperation* op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%s---3--%@---",__func__, [NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"%s---4--%@---",__func__, [NSThread currentThread]);
}];
// 3. 添加到队列操作
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
// 直接添加任务到队列,简写
[queue addOperationWithBlock:^{
NSLog(@"%s---5--%@---",__func__, [NSThread currentThread]);
}];
/**
执行结果 : 并发,在子线程中执行
{number = 4, name = (null)}---
{number = 3, name = (null)}---
{number = 6, name = (null)}---
{number = 5, name = (null)}---
*/
}
3. NSOperationQueue队列 + 自定义子类实现
-(void)touchesBegan4:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// 1. 创建队列
NSOperationQueue* queue = [[NSOperationQueue alloc ]init];
XMGOperation* op1= [[XMGOperation alloc] init];
[queue addOperation:op1];
/**
输出内容 :在子线程中 执行
{number = 6, name = (null)}
*/
}
3. NSOperationQueue队列 + NSOperation子类 实现串行 任务
通过设置 queue.maxConcurrentOperationCount= 1; 设置任务串行执行,只开一个子线程
-(void)touchesBegan5:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// 1. 创建队列
NSOperationQueue* queue = [[NSOperationQueue alloc ]init];
// 串行队列, 只开一个子线程
// 什么是串行: 就是按照 1,2,3 任务依次 执行
// -1 无限制 最大并发数
queue.maxConcurrentOperationCount= 1;
// 2. 封装操作
NSBlockOperation* op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%s---1--%@---",__func__, [NSThread currentThread]);
}];
NSBlockOperation* op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%s---2--%@---",__func__, [NSThread currentThread]);
}];
NSBlockOperation* op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%s---3--%@---",__func__, [NSThread currentThread]);
}];
// 3. 添加到队列操作
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
}
4. 暂停队列中任务:
(self.queue.suspended): 暂停任务 ,可以就行执行
[self.queue cancelAllOperations]; 取消所有任务,不可恢复
-(void)touchesBegan6:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// 当值 为 YES的 时候回暂停, 为NO的时候恢复
if(self.queue.suspended){ // 如果是暂停
NSLog(@"恢复执行");
self.queue.suspended= NO;
}else{
NSLog(@"暂停执行");
self.queue.suspended=YES;
}
// 注意:如果 op2已经在执行,执行 self.queue.suspended=YES; 不会暂停op2
// 而是等待op2 执行完毕以后暂停后面的 op3
// [self.queue cancelAllOperations]; // 取消不在执行了,不可逆的
}
- (IBAction)btn:(id)sender {
// 1. 创建队列
NSOperationQueue* queue = [[NSOperationQueue alloc ]init];
// 串行队列, 只开一个子线程
// 什么是串行: 就是按照 1,2,3 任务依次 执行
// -1 无限制 最大并发数
queue.maxConcurrentOperationCount= 1;
// 2. 封装操作
NSBlockOperation* op1 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"%s---1--%@---",__func__, [NSThread currentThread]);
}];
NSBlockOperation* op2 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"%s---2--%@---",__func__, [NSThread currentThread]);
}];
NSBlockOperation* op3 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"%s---3--%@---",__func__, [NSThread currentThread]);
}];
// 3. 添加到队列操作
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
self.queue= queue;
}
5. 暂停 自定义 子类实现
MGOperation.h 和 MGOperation.m
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface XMGOperation : NSOperation
@end
NS_ASSUME_NONNULL_END
===================================================
#import "XMGOperation.h"
@implementation XMGOperation
- (void)main{
// ....
// 一万 行代码 .....
// ----XMGOperation----<NSThread: 0x600002a9d9c0>{number = 1, name = main}
for(NSInteger i=0;i<100;i++){
[NSThread sleepForTimeInterval:0.1];
NSLog(@"1--%zd--%@",i,[NSThread currentThread]);
}
// 不建议 放在 for 循环中每次 都判断
// 执行完一段 耗时操作以后,判断是已经 调用cancle 方法, 取消任务
if(self.isCancelled){
return;
}
for(NSInteger i=0;i<100;i++){
[NSThread sleepForTimeInterval:0.1];
NSLog(@"2--%zd--%@",i,[NSThread currentThread]);
}
if(self.isCancelled){
return;
}
for(NSInteger i=0;i<100;i++){
[NSThread sleepForTimeInterval:0.1];
NSLog(@"3--%zd--%@",i,[NSThread currentThread]);
}
if(self.isCancelled){
return;
}
}
@end
调用:
- (IBAction)btn:(id)sender {
// 1. 创建队列
NSOperationQueue* queue = [[NSOperationQueue alloc ]init];
XMGOperation * op1 = [[XMGOperation alloc] init];
[queue addOperation:op1];
self.queue=queue;
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// 如果自定义operation 那么 无法取消任务 调用 cancelAllOperations,
// 苹果官方建议,在main 中判断 是否已经 调用取消
[self.queue cancelAllOperations];
}
6. NSOperation 操作依赖 , 类似 gcd 的栅栏 函数
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// 1. 创建队列
NSOperationQueue* queue = [[NSOperationQueue alloc ]init];
NSOperationQueue* queue1 = [[NSOperationQueue alloc ]init];
// 2. 封装操作
NSBlockOperation* op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%s---1--%@---",__func__, [NSThread currentThread]);
}];
NSBlockOperation* op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%s---2--%@---",__func__, [NSThread currentThread]);
}];
NSBlockOperation* op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%s---3--%@---",__func__, [NSThread currentThread]);
}];
NSBlockOperation* op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%s---4--%@---",__func__, [NSThread currentThread]);
}];
// 添加操作依赖
[op2 addDependency:op1]; // 操作2 一定在 操作1 后面执行
[op3 addDependency:op2]; // 操作3 一定在 操作2 后面执行
op2.completionBlock=^(){
NSLog(@"%@",@"op2已经完成了"); // 依赖监听
};
[op4 addDependency:op3]; // 操作4 一定在 操作3 后面执行,操作依赖可以跨队列
// 3. 添加到队列操作
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue1 addOperation:op4];
/** 输出结果 :
_block_invoke---1--
_block_invoke---2--
_block_invoke_3---3--
op2已经完成了
_block_invoke_4---4-
*/
}
7. NSOperation 线程之间通信
案例,下载单张图片
下载2张图片,图片合成
7.1. 下载单张图片
-(void)touchesBegan8:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSOperationQueue* queue = [[NSOperationQueue alloc ]init];
NSBlockOperation* op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%s---1--%@---",__func__, [NSThread currentThread]);
NSURL* url= [NSURL URLWithString:@"http://wimg.spriteapp.cn/cropx/852x480/cutbimage/5ed5b653d19fd.jpg"];
// 2. 下载图片
NSData* data= [NSData dataWithContentsOfURL:url];
// 3. 把 二进制图片 转化为图片
UIImage* image= [UIImage imageWithData:data];
// 4. 刷新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.myImageView.image = image;
}];
}];
[queue addOperation:op1];
}
2. 下载2张图片,图片合成
@interface ViewController1 ()
@property(nonatomic,strong) NSOperationQueue* queue;
@property (weak, nonatomic) IBOutlet UIImageView *myImageView;
@property(nonatomic,strong) UIImage* image1;
@property(nonatomic,strong) UIImage* image2;
@end
@implementation ViewController1
// 图片合成
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSOperationQueue* queue = [[NSOperationQueue alloc ]init];
NSBlockOperation* download1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%s---1--%@---",__func__, [NSThread currentThread]);
NSURL* url= [NSURL URLWithString:@"http://wimg.spriteapp.cn/cropx/852x480/cutbimage/5ed5b653d19fd.jpg"];
// 2. 下载图片
NSData* data= [NSData dataWithContentsOfURL:url];
// 3. 把 二进制图片 转化为图片
UIImage* image= [UIImage imageWithData:data];
self.image1= image;
}];
NSBlockOperation* download2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%s---2--%@---",__func__, [NSThread currentThread]);
NSURL* url= [NSURL URLWithString:@"http://img5.mtime.cn/mg/2019/06/27/224744.68512147_120X90X4.jpg"];
// 2. 下载图片
NSData* data= [NSData dataWithContentsOfURL:url];
// 3. 把 二进制图片 转化为图片
UIImage* image= [UIImage imageWithData:data];
self.image2= image;
}];
[queue addOperation:download1];
[queue addOperation:download2];
NSBlockOperation *combie = [NSBlockOperation blockOperationWithBlock:^{
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
[self.image1 drawInRect:CGRectMake(0, 0, 200, 100)];
[self.image2 drawInRect:CGRectMake(0, 100, 200, 100)];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//刷新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.myImageView.image = image;
NSLog(@"%@----",[NSThread currentThread]);
}];
}];
[combie addDependency:download1];
[combie addDependency:download2];
[queue addOperation:combie];
}
@end