NSOperation简介
熟悉了解GCD之后,对于NSOperation的理解和使用相对简单一点。首先,NSOperation是基于GCD面向对象的封装,是一种“并发”技术,之所以是“并发”技术,后面会有详细的代码演示。
- 队列:NSOperationQueue
- 操作:NSOperation (下面提到的“操作”指的就是NSOperation)
NSOperation位于Foundation系统框架下,是一个抽象类,本身不能直接使用,它的作用是为其子类提供了一些共有属性,我们所用到的都是其子类:
NSIvocationoperation 方法选择器方式操作,将要执行的繁琐的操作封装为单独的方法,使得代码规范条理;
NSBlockOperation Block方式操作,将要执行的简单的操作作为代码块方式直接添加,所有代码会写在一个地方,便于维护。
NSOperation的所有子类都可以直接添加到NSOperationQueue对象中,自动立即异步执行操作。
NSOperation简单使用
- NSOperation的两个子类的使用
(1)NSInvocationOperation:方法选择式
NSInvocationOperation *op = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@"invocation"];
- (void)download:(id)objc{
NSLog(@"%@ %@",[NSThread currentThread],objc);
}
start
方式,会在当前线程执行调度方法
[op start];
addOperation:
方式,将操作添加到队列,这种添加方式会自动异步调度方法。为了清晰查看异步调度,我们加入循环并打印结果:
@property (strong, nonatomic) NSOperationQueue *q;
#pragma mark -------- 懒加载
- (NSOperationQueue *)q{
if (!_q) {
_q = [[NSOperationQueue alloc]init];
}
return _q;
}
for (int i = 0; i < 10 ; i ++) {
NSInvocationOperation *op = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@(i)];
[self.q addOperation:op]; }
打印
2018-04-08 11:28:10.687493+0800 NSOperation[1308:362486] {number = 3, name = (null)} 0
2018-04-08 11:28:10.687507+0800 NSOperation[1308:362485] {number = 4, name = (null)} 1
2018-04-08 11:28:10.687496+0800 NSOperation[1308:362488] {number = 6, name = (null)} 3
2018-04-08 11:28:10.687493+0800 NSOperation[1308:362487] {number = 5, name = (null)} 2
2018-04-08 11:28:10.687844+0800 NSOperation[1308:362488] {number = 6, name = (null)} 4
2018-04-08 11:28:10.687849+0800 NSOperation[1308:362486] {number = 3, name = (null)} 5
2018-04-08 11:28:10.687864+0800 NSOperation[1308:363127] {number = 7, name = (null)} 6
2018-04-08 11:28:10.688220+0800 NSOperation[1308:363128] {number = 8, name = (null)} 7
2018-04-08 11:28:10.688495+0800 NSOperation[1308:363129] {number = 9, name = (null)} 8
2018-04-08 11:28:10.688569+0800 NSOperation[1308:362487] {number = 5, name = (null)} 9
异步调度,非顺序执行
如果同时添加几个操作可以使用addOperations
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@(i)];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@(i)];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@(i)];
NSInvocationOperation *op4 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download:) object:@(i)];
[self.q addOperations:@[op1, op2, op3, op4]];
注意:
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait
这个方法中有一个wait参数 意思是:这段代码之后的代码是否等待 会卡主当前线程
(2)NSBlockOperation:
NSBlockOperation同样有start、add 和addOperations 三种方式,这里不再列举,此处简单介绍实例化方式:
for (int i = 0; i < 10 ; i ++) {
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@ -- %d",[NSThread currentThread],i);
}];
[self.q addOperation:op];
}
打印结果和NSInvocationOperation一样
2.NSOperationQueue直接添加操作Block,无需创建操作
for (int i = 0; i < 10; i ++) {
[self.q addOperationWithBlock:^{
NSLog(@"%@ -- %d",[NSThread currentThread],i);
}];
}
- NSOperationQueueNSOperationQueue的相关属性 最大并发数:
maxConcurrentOperationCount
、暂停/继续suspended
、队列的操作数:operationCount
、取消队列所有操作:cancelAllOperations
。
- maxConcurrentOperationCount:
self.q.maxConcurrentOperationCount = 2;
for (int i = 0; i < 10; i ++) {
[self.q addOperationWithBlock:^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"%@ %d",[NSThread currentThread],i);
}];
}
打印
2018-04-08 14:19:47.632013+0800 NSOperation最大并发数[1694:663455] {number = 3, name = (null)} 0
2018-04-08 14:19:47.632013+0800 NSOperation最大并发数[1694:663452] {number = 4, name = (null)} 1
2018-04-08 14:19:48.633749+0800 NSOperation最大并发数[1694:663454] {number = 5, name = (null)} 2
2018-04-08 14:19:48.633749+0800 NSOperation最大并发数[1694:663452] {number = 4, name = (null)} 3
2018-04-08 14:19:49.634068+0800 NSOperation最大并发数[1694:663454] {number = 5, name = (null)} 4
2018-04-08 14:19:49.634194+0800 NSOperation最大并发数[1694:663455] {number = 3, name = (null)} 5
2018-04-08 14:19:50.639034+0800 NSOperation最大并发数[1694:663454] {number = 5, name = (null)} 7
2018-04-08 14:19:50.639026+0800 NSOperation最大并发数[1694:663452] {number = 4, name = (null)} 6
2018-04-08 14:19:51.639502+0800 NSOperation最大并发数[1694:663452] {number = 4, name = (null)} 9
2018-04-08 14:19:51.639502+0800 NSOperation最大并发数[1694:663455] {number = 3, name = (null)} 8
最大并发数设置完成之后,队列调度操作会开启子线程的条数是不确定的(原因是当子线程在执行完成操作之后会有一个回收线程的过程,在此过程中,队列调度操作仍在继续,会到线程池中拿到线程执行操作),确定的是正在执行任务的子线程的条数就是小于或等于最大并发数。
iOS8.0以前,无论使用GCD还是NSOperation,都会开启很多条线程,在iOS7.0 以前,GCD通常只会开启5~6线程!
目前线程多了,说明:
1)底层的线程池更大了,能拿到的线程资源多了
2)对控制同事并发的线程数,需求更高了
- suspended:
//暂停
self.q.suspended = YES;
//继续
self.q.suspended = NO;
当执行暂停操作时,正在执行的操作不会被暂停
- operationCount
- cancelAllOperations
1)队列挂起的时候,不会清空内部的操作。只有队列继续的时候才会清空
2)正在执行的操作也不会被取消
- NSOperation依赖关系
在GCD中,我们通过同步的方式,给多个任务添加依赖关系。在NSOperation中,苹果提供了更简单的面向Operation对象的依赖关系设置addDependency
。下面依然拿GCD中用到的例子:登录、支付、下载来演示操作之间的依赖关系设置。
/**
例子:登录、支付、下载
*/
//1.登录
NSBlockOperation *opLogIn = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"登录--%@",[NSThread currentThread]);
}];
//2.支付
NSBlockOperation *opPay = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"支付--%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:10.0];
}];
//3.下载
NSBlockOperation *opDown = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载--%@",[NSThread currentThread]);
}];
//4.完成
NSBlockOperation *opFinish = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"完成--%@",[NSThread currentThread]);
}];
//支付依赖于登录
[opPay addDependency:opLogIn];
//下载依赖于支付
[opDown addDependency:opPay];
[self.opQueue addOperations:@[opLogIn,opPay,opDown] waitUntilFinished:YES];
NSLog(@"come here!%@",[NSThread currentThread]);
注意:不要设置循环依赖,循环依赖的操作不会执行;一定要在操作添加到队列之前设置依赖关系,否则无效
将完成操作放到主线程中执行,完成线程间通讯
[[NSOperationQueue mainQueue] addOperation:opFinish];