前面介绍了多线程中常用的GCD,链接:iOS多线程网络之GCD,这次介绍一下多线程网络中的另一个好用的技术NSOperation
简介
NSOperation和GCD差不多,但是NSOperation更加的面向对象,相当于对GCD的一个封装,NSOperation本身是一个抽象类,不具有封装操作的能力,即定义一个NSOperation对象不能进行多线程操作,要想实现操作,必须要使用到它的子类:NSInvocationOperation或NSBlockOperation,但是在使用其进行多线程操作的时候,必须先将其加入到NSOperationQueue队列中
使用方法
NSOperationQueue
NSOperationQueue队列类似于GCD中的主队列,GCD中队列分为:主队列,串行队列,并行队列,而NSOperationQueue只有两种:主队列,非主队列
- 主队列
我们没有办法直接创建主队列,但是可以通过创建非主队列然后获取到主队列
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
加入到主队列的任务和GCD相同,不同创建新的线程,任务顺序执行
- 非主队列
我们通过NSOperationQueue的init方法创建的队列都为非主队列,即我们自定义的队列都为非主队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
加入到非主队列的任务会自动创建新的线程执行任务,任务并行执行,加入到队列中的任务不用再调用start方法来执行,系统会创建线程自动执行
NSInvocationOperation
NSInvocationOperation直接使用init的创建方法,需要实现自定义的方法
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(click1) object:nil];
定义好对象之后,调用start方法执行任务,此时定义的所有对象都是同步操作的,即不会开启新的线程执行任务
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(click1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(click2) object:nil];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(click3) object:nil];
[op1 start];
[op2 start];
[op3 start];
执行结果:
但是当我们将对象加入到NSOperationQueue队列中后,即可开启新的线程执行任务
[queue addOperations:@[op1, op2, op3] waitUntilFinished:YES];
执行结果:
NSBlockOperation
NSBlockOperation采用block的创建方式,直接在block块中书写任务
NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block1-------%@",[NSThread currentThread]);
}];
定义好之后,调用start方法执行任务,但是同样的此时不会开启新的线程
NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block1-------%@",[NSThread currentThread]);
}];
NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block2-------%@",[NSThread currentThread]);
}];
NSBlockOperation *block3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block3-------%@",[NSThread currentThread]);
}];
[block1 start];
[block2 start];
[block3 start];
执行结果:
当我们将其加入到队列中之后,即可开启新的线程
[queue addOperations:@[block1, block2, block3] waitUntilFinished:YES];
执行结果:
使用NSBlockOperation的时候,我们还可以对其追加block任务,同样可以并发执行
[block1 addExecutionBlock:^{
NSLog(@"block11--------%@",[NSThread currentThread]);
}];
执行结果:
当然,我们使用NSBlockOperation的时候也可以创建一个没有任务的NSBlockOperation对象,然后对其进行追加.
那么问题来了,将两种对象加入到队列中能不能实现并行执行呢?
答案是可以
[queue addOperations:@[block1, block2, block3, op1, op2, op3] waitUntilFinished:YES];
执行结果:
最大并发数
NSOperation和GCD的一个区别就是可以设置并发数量,我们可以通过设置并发数量,来限制是否进行多线程操作
queue.maxConcurrentOperationCount = 5;
和GCD的区别
NSOperation既然是对GCD的封装,那么必然有着GCD不能实现的功能
取消任务
当我们设置了两个任务A和B,如果B要在A任务完成后使用A获得的数据进行任务,但是A任务因为网络问题执行失败,那么我们应该取消B任务来节省性能
//取消NSOperation任务
[op1 cancel];
//取消NSOperationQueue剩余的NSOperation任务
[queue cencelAllOperations]
设置依赖关系
还是A和B的例子,如果B必须要在A执行完之后再进行,如果是在主线程中,这个肯定是实现的,但是我们如果还想实现多线程,还是满足条件,怎么办呢?GCD中可以使用栅栏函数来实现这一功能,但是NSOperation更为简单,直接设置依赖即可,前提是两个任务都在队列中
//op2在op1执行完成之后执行
[op2 addDependency:op1];
注意:两者不能相互依赖,即op1依赖于op2,op2依赖于op1,这样会导致死锁
队列的暂停与恢复
这个更加简单,直接设置属性即可
[queue setSuspended:YES];//YES代表暂停队列,NO代表恢复队列
定义任务优先级
在GCD中,我们可以定义队列执行的优先级,但是在NSOperation中,我们可以定义任务的优先级,关于任务优先级,有五种类型:
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
我们可以直接设置任务对象的优先级,但是设置了优先级一定就能保证优先执行吗?如果多个任务对象的优先级相同会怎么样呢?
op1.queuePriority = NSOperationQueuePriorityVeryHigh;
op2.queuePriority = NSOperationQueuePriorityVeryHigh;
op3.queuePriority = NSOperationQueuePriorityVeryHigh;
[queue addOperations:@[op1, op2, op3] waitUntilFinished:YES];
执行结果:
恩?好像是谁先设置谁先执行?别高兴的太早,我们再次运行试试看
这个时候就不一样了,这个需要注意
如何选择?
通过两篇文章的总结,我们可以注意到GCD通过block为单位,结构和C语言相似,更加的底层,处理相对较快,且使用简便,设置单个任务比NSOperation要简单.
而NSOperation可以设置并发数来限制线程,任务执行的顺序且任务可以被取消,队列可以暂停和恢复,更加的灵活.,对于多个任务的处理比GCD要更好控制.
欢迎来到我的博客,我是AnICoo1,我喂自己袋盐
有错误请评论指出或联系我,不胜感激!
个人邮箱:helloiamclh@gmail.com