NSOperation的作用
- 配合使用NSOperation和NSOperationQueue也能实现多线程编程
NSOperation和NSOperationQueue实现多线程的具体步骤
- 先将需要执行的操作封装到一个NSOperation对象中
- 然后将NSOperation对象添加到NSOperationQueue中
- 系统会自动将NSOperationQueue中的NSOperation取出来
- 将取出的NSOperation封装的操作放到一条新线程中执行
NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
- 使用NSOperation子类的方式有3种:
- NSInvocationOperation
- NSBlockOperation
- 自定义子类继承NSOperation,实现内部相应的方法
NSInvocationOperation
- 创建NSInvocationOperation对象
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
- 调用start方法开始执行操作
- (void)start;
一旦执行操作,就会调用target的sel方法
- 注意:
- 默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
- 只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
- 操作
- (void)invocation
{
NSInvocationOperation*op1=[[NSInvocationOperation alloc]initWithTarget:selfselector:@selector(demo)object:nil];
//如果直接执行NSInvocationOperation中的操作,那么默认会在主线程中执行
[op1 start];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];
[op2 start];
}
NSBlockOperation
- 创建NSBlockOperation对象
+ (id)blockOperationWithBlock:(void (^)(void))block;
- 通过addExecutionBlock:方法添加更多的操作
- (void)addExecutionBlock:(void (^)(void))block;
注意:
只要NSBlockOperation封装的操作数 > 1,就会异步执行操作- 使用
- (void)blockOperation
{
NSBlockOperation * op1=[NSBlockOperation blockOperationWithBlock:^{
NSLog(@"create----%@",[NSThread currentThread]);
}];
[op1 addExecutionBlock:^{
NSLog(@"add1-----%@",[NSThread currentThread]);
}];
[op1 start];
//如果只封装了一个操作, 那么默认会在主线程中执行
//如果封装了多个操作, 那么除了第一个操作以外, 其它的操作会在子线程中执行
}
output:
2016-06-27 16:04:52.512 test1[9640:2351301] create----<NSThread: 0x7fea43e008f0>{number = 1, name = main}
2016-06-27 16:04:52.512 test1[9640:2351344] add1-----<NSThread: 0x7fea43d19480>{number = 2, name = (null)}
GCD的队列类型
- 并发队列:自己创建,全局
- 串行队列:主队列自己创建,自己创建的
NSOperationQueue
- 自己创建: alloc/init --> 默认是并发 --> 也可以让它串行
- 主队列 : mainQueue
- NSOperationQueue的作用
- NSOperation可以调用start方法来执行任务,但默认是同步执行的
如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
- 添加操作到NSOperationQueue中
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block
- 使用
- (void)block
{
NSOperationQueue * queue=[[NSOperationQueue alloc]init];
[queue addOperationWithBlock:^{
NSLog(@"block1----%@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"block2----%@",[NSThread currentThread]);
}];
}
output:
2016-06-27 16:35:30.415 test1[9859:2380633] block2----<NSThread: 0x7f88f1f13550>{number = 3, name = (null)}
2016-06-27 16:35:30.415 test1[9859:2380610] block1----<NSThread: 0x7f88f1d0f530>{number = 2, name = (null)}
- (void)invation
{
//创建两个操作
NSInvocationOperation * op1=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(test1) object:nil];
NSInvocationOperation * op2=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(test2) object:nil];
//创建一个队列,如果创建的是mainQueue那么就是在主队列中执行,不然就是在并行队列中执行
NSOperationQueue * queue=[[NSOperationQueue alloc]init];
[queue addOperation:op1];
[queue addOperation:op2];
}
- (void)test1
{
for (int i=0; i<5; i++) {
NSLog(@"test1---%@",[NSThread currentThread]);
}
}
- (void)test2
{
for (int i=0; i<5; i++) {
NSLog(@"test2---%@",[NSThread currentThread]);
}
}
output:
2016-06-27 16:55:05.804 test1[9937:2389981] test2---<NSThread: 0x7f9ee062d150>{number = 3, name = (null)}
2016-06-27 16:55:05.804 test1[9937:2389978] test1---<NSThread: 0x7f9ee0704a20>{number = 2, name = (null)}
2016-06-27 16:55:05.806 test1[9937:2389978] test1---<NSThread: 0x7f9ee0704a20>{number = 2, name = (null)}
2016-06-27 16:55:05.806 test1[9937:2389981] test2---<NSThread: 0x7f9ee062d150>{number = 3, name = (null)}
2016-06-27 16:55:05.807 test1[9937:2389978] test1---<NSThread: 0x7f9ee0704a20>{number = 2, name = (null)}
2016-06-27 16:55:05.807 test1[9937:2389981] test2---<NSThread: 0x7f9ee062d150>{number = 3, name = (null)}
2016-06-27 16:55:05.807 test1[9937:2389978] test1---<NSThread: 0x7f9ee0704a20>{number = 2, name = (null)}
2016-06-27 16:55:05.808 test1[9937:2389981] test2---<NSThread: 0x7f9ee062d150>{number = 3, name = (null)}
2016-06-27 16:55:05.808 test1[9937:2389978] test1---<NSThread: 0x7f9ee0704a20>{number = 2, name = (null)}
2016-06-27 16:55:05.808 test1[9937:2389981] test2---<NSThread: 0x7f9ee062d150>{number = 3, name = (null)}
最大并发数
- 什么是并发数
同时执行的任务数
比如,同时开3个线程执行3个任务,并发数就是3- 最大并发数的相关方法
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
队列的取消、暂停、恢复
- 取消队列的所有操作
- (void)cancelAllOperations;
提示:也可以调用NSOperation的- (void)cancel方法取消单个操作
暂停和恢复队列
- (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
- (BOOL)isSuspended;
操作依赖
- NSOperation之间可以设置依赖来保证执行顺序
- 比如一定要让操作A执行完后,才能执行操作B,可以这么写
[operationB addDependency:operationA]; // 操作B依赖于操作A
- 使用,使用两个图片拼接这种典型
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self setUp];
}
- (void)setUp
{
__block UIImage *image1=nil;
__block UIImage *image2=nil;
//创建一个异步队列
NSOperationQueue *queue=[[NSOperationQueue alloc]init];
//开启一个子线程下载图片
NSOperation *op1=[NSBlockOperation blockOperationWithBlock:^{
NSURL *url=[NSURL URLWithString:@"http://s.wasu.cn/data/images/201604/13/570e0b67c15fb.jpg"];
NSData *date=[NSData dataWithContentsOfURL:url];
image1=[UIImage imageWithData:date];
NSLog(@"---op1----%@",[NSThread currentThread]);
}];
//下载另一个图片
NSOperation *op2=[NSBlockOperation blockOperationWithBlock:^{
NSURL *url=[NSURL URLWithString:@"http://i1.hdslb.com/bfs/archive/5e91addadc849572575b592814846977ab5559f1.jpg"];
NSData *date=[NSData dataWithContentsOfURL:url];
image2=[UIImage imageWithData:date];
NSLog(@"---op2----%@",[NSThread currentThread]);
}];
//拼接图片
NSOperation *op3=[NSBlockOperation blockOperationWithBlock:^{
CGSize size=CGSizeMake(300, 400);
UIGraphicsBeginImageContext(size);
[image1 drawAsPatternInRect:CGRectMake(0, 0, 150,400)];
[image2 drawAsPatternInRect:CGRectMake(150, 0, 150, 400)];
UIImage * image=UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
NSLog(@"进入主线程");
self.imageView.image=image;
}];
NSLog(@"---op3----%@",[NSThread currentThread]);
}];
op1.completionBlock=^{
NSLog(@"第一张图下载完毕");
};
op2.completionBlock=^{
NSLog(@"第二张图下载完毕");
};
//依赖要放在添加队列之前
[op3 addDependency:op1];
[op3 addDependency:op2];
NSArray *opreations=[NSArray arrayWithObjects:op1,op2,op3, nil];
[queue addOperations:opreations waitUntilFinished:NO];
}
@end
output:
2016-06-27 17:35:39.651 图片合成[10336:2417737] ---op2----<NSThread: 0x7fe702c4c4e0>{number = 3, name = (null)}
2016-06-27 17:35:39.651 图片合成[10336:2417736] 第二张图下载完毕
2016-06-27 17:35:40.528 图片合成[10336:2417738] ---op1----<NSThread: 0x7fe702e80fa0>{number = 4, name = (null)}
2016-06-27 17:35:40.529 图片合成[10336:2417781] 第一张图下载完毕
2016-06-27 17:35:40.552 图片合成[10336:2417666] 进入主线程
2016-06-27 17:35:40.552 图片合成[10336:2417737] ---op3----<NSThread: 0x7fe702c4c4e0>{number = 3, name = (null)}
- 可以在不同queue的NSOperation之间创建依赖关系
操作的监听
- 可以监听一个操作的执行完毕
- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;
自定义NSOperation
- 自定义NSOperation的步骤很简单
重写- (void)main方法,在里面实现想执行的任务
- 重写- (void)main方法的注意点
- 自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应