概述
NSOperation 是苹果公司对 GCD 的封装,完全面向对象,所以使用起来更好理解。 大家可以看到 NSOperation 和 NSOperationQueue 分别对应 GCD 的 任务
和 队列
。
操作步骤主要就两步:
- 将要执行的任务封装到
NSOperation
中; - 将此任务添加到
NSOperationQueue
中。
添加任务
NSOperation
只是一个抽象类,所以不能封装任务。但它有 2 个子类用于封装任务。分别是:NSInvocationOperation
和 NSBlockOperation
。创建一个 Operation 后,需要调用 start 方法来启动任务,它会 默认在当前队列同步执行。当然你也可以在中途取消一个任务,只需要调用其 cancel
方法即可。
- NSInvocationOperation :初始化
//1.创建NSInvocationOperation对象 NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil]; //2.开始执行 [operation start];
- NSBlockOperation: 初始化
//1.创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//2.开始任务
[operation start];
> 这样添加的任务,会默认在当前线程执行。不过```NSBlockOperation```提供了这个方法```addExecutionBlock:```,可以给operation添加多个block,这样operation中的任务就会**并发执行**,。他会在**主线程和其他多个线程**执行这些任务
注:```addExecutionBlock``` 方法必须在 start() 方法之前执行,否则就会报错:
‘*** -[NSBlockOperation addExecutionBlock:]: blocks cannot be added after the operation has started executing or finished'
### 添加队列
我们通过``` NSOperation``` 对象的``` start() ```方法来启动这个任务,事实上系统默认是 **同步执行** 的。就算是 ```addExecutionBlock``` 方法,也会在 **当前线程和其他线程 中执行**,也就是说还是会占用当前线程。这时就要用到队列 **NSOperationQueue** 了。一般分为两种队列:主队列、其他队列。**只要添加到队列,会自动调用任务的 start() 方法**。
- 主队列
通过调用```mainQueue```类方法创建的是为主队列。
NSOperationQueue *queue = [NSOperationQueue mainQueue];
- 其他队列
因为主队列比较特殊。个人觉得是主队列是单例,所以统一用一个类方法来返回。通过初始化产生的队列,就是其他队列。其他队列的任务会在其他线程并行执行
//1.创建一个其他队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2.创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//3.添加多个Block
for (NSInteger i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}
//4.队列添加任务
[queue addOperation:operation];
#####需要注意
运行上面代码,会发现这里并没有串行队列,和GCD并不同。如果想要实现,则可以设置```NSOperationQueue```中```maxConcurrentOperationCount```这个属性。该属性是设置最大并发数,用来设置最多可以让多少个任务同时执行。因此设置为```1```即可。
```NSOperationQueue``` 还有一个快速添加任务的方法,```- (void)addOperationWithBlock:(void (^)(void))block;```这样就可以添加一个任务到队列中了,十分方便。
#### 依赖
**场景**
比如有 3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。这时就可以用到依赖了
//1.任务一:下载图片
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载图片 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//2.任务二:打水印
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"打水印 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//3.任务三:上传图片
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"上传图片 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//4.设置依赖
[operation2 addDependency:operation1]; //任务二依赖任务一
[operation3 addDependency:operation2]; //任务三依赖任务二
//5.创建队列并加入任务
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
使用注意:
- 不能相互依赖, A依赖B,B依赖A,这样会造成死锁
- 可以使用方法```removeDependency ```解除依赖
- 依赖是添加到任务上的。因此,依赖是跨队列的。
> 未完待续