一. 定义
NSOperation
是苹果公司提供的一套多线程解决方案, 它是基于GCD
的更抽象的"面向对象"封装.
二. 对比GCD
- 支持任务之间添加依赖关系, 控制执行顺序
- 提供可选实现的任务完成
block
- 可使用KVO监听任务执行状态变化
- 支持设置任务的优先级, 可控制任务的相对执行顺序
- 提供取消操作
三. 任务
任务就是在线程中执行的代码, 在GCD中表现形式是block
, 在NSOperationshi是在其子类NSInvocationOperation, NSBlockOperation, 自定义子类中执行任务代码.
四. 队列
NSOperationQueue表示队列, 用来存放任务的队列
- NSOperation单独使用时是同步操作, 配合NSOperationQueue才能实现异步操作
- GCD添加到队列中的任务遵守
FIFO
原则, 对于添加到NSOperationQueue中的任务, 首先根据任务之间的依赖关系决定任务的就绪状态, 进入就绪状态的任务由任务之间的相对优先级决定执行顺序 - NSOperationQueue可以设置最大并发任务数量
- NSOperationQueue 提供两种队列: 主队列和自定义队列,
- 主队列在主线程运行
- 自定义队列在后台执行
五. 基本使用
NSOperation
是一个抽象类, 使用的时候必须子类化, 提供三种方式创建任务:
1. 使用子类NSInvocationOperation
NSInvocationOperation *invocationOpeartion = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOpeartion) object:nil];
// 如果没有添加到 NSOperationQueue, 则需要手动调用 start
[invocationOpeartion start];
/// 回调方法
- (void)invocationOpeartion {
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}
注意: NSInvocationOperation单独使用时, 并没有开启新的线程, 任务都是在当前线程中执行
2. 使用子类NSBlockOperation
- 2.1 简单使用
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// 任务代码
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}];
[blockOperation start];
注意: NSBlockOperation单独使用时, 并没有开启新的线程, 任务都是在当前线程中执行
- 2.2: NSBlockOperation类有一个实例方法
addExecutionBlock
, 这个方法可以添加一个代码块, 当执行NSBlockOperation
对象start
时, 该对象将其所有块提交给默认优先级的并发调度队列. 然后对象等待所有的块完成执行. 当最后一个块完成执行时, 操作对象将自己标记为已完成. 由于块操作本身在单独的线程上运行, 所以应用程序的其他线程可以在等待块操作完成的同时继续工作.
需要说明的一点是, 如果添加的任务较多的话, 这些操作(包括blockOperationWithBlock中的操作)可能在不同的线程中并发执行, 这是由系统决定的
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// 任务代码
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}];
[blockOperation addExecutionBlock:^{
NSLog(@"addExecutionBlock1:%@", NSThread.currentThread);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"addExecutionBlock2:%@", NSThread.currentThread);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"addExecutionBlock3:%@", NSThread.currentThread);
}];
[blockOperation start];
在调用了addExecutionBlock方法添加了多组任务后,开启新的线程,任务是并发执行的,blockOperationWithBlock中的任务执行是否在当前的线程执行, 统一由系统调度.
3. 自定义继承自NSOperation
的子类
@interface Operation : NSOperation
@end
@implementation Operation
-(void)main {
if (!self.isCancelled) {
for (int i = 0; i < 4; i++) {
sleep(2);
NSLog(@"%d==%@", i, NSThread.currentThread);
}
}
}
@end
// 使用:
Operation *op = [[Operation alloc] init];
[op start];
注意: 自定义的Operation并没有开启新的线程,任务的执行是在当前的线程中执行的。
六. 队列: NSOperationQueue
NSOperationQueue
提供了主队列和自定义队列
- 主队列:
[NSOperationQueue mainQueue];
, 凡是添加到主队列的NSOperation
任务都会放到主线程执行 - 自定义队列:
[[NSOperationQueue alloc] init]
, 凡是添加到自定义队列的NSOperation
任务都会放到子线程执行.
1.添加任务到队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 方式1:
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// 任务代码
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}];
[queue addOperation:blockOperation];
// 方式2:
[queue addOperationWithBlock:^{
// 任务代码
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}];
// 注意: 不需要调用NSBlockOperation
的 start
方法
2.添加多个操作到队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 操作1
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// 任务代码
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}];
// 操作2
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];
// 方式1:
[queue addOperation:blockOperation];
[queue addOperation:invocationOperation];
// 方式2:
// waitUntilFinished参数,如果传YES,则表示会等待队列里面的任务执行完成后才会往下执行,也就是会阻塞线程
[queue addOperations:@[blockOperation, invocationOperation] waitUntilFinished:true];
/// 回调方法
- (void)invocationOperation {
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
}
总结: waitUntilFinished
参数,如果传YES
,则表示会等待队列里面的任务执行完成后才会往下执行,也就是会阻塞线程
七. 同步&并发
NSOperationQueue
有个一属性maxConcurrentOperationCount
maxConcurrentOperationCount
默认-1, 不做限制, 在子线程中执行任务
maxConcurrentOperationCount
= 1, 队列中的任务会同步执行, 阻塞当前线程
maxConcurrentOperationCount
> 1, 队列中的任务会并发执行, 开启子线程执行
maxConcurrentOperationCount
值并不是表示并发执行的线程数量,而是在一个队列中能够同时执行的任务的数量。
八. NSOperation线程间的通讯, 子线程完成任务, 会主线程更新UI
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
// 任务代码
for (int i = 0; i < 5; i++) {
sleep(2);
NSLog(@"%d -- %@", i, NSThread.currentThread);
}
// 回主队列更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"更新UI");
}];
}];
[queue addOperation:blockOperation];
九. 任务的依赖关系
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
// 任务代码
NSLog(@"1");
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2");
}];
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3");
}];
// 1依赖于2和3, 意思是在2和3都执行完后才会执行1;
[operation1 addDependency:operation2];
[operation1 addDependency:operation3];
// 将1的优先级设置最高
operation1.queuePriority = NSOperationQueuePriorityHigh;
[queue addOperations:@[operation1, operation2, operation3] waitUntilFinished:NO];
//log: 2 3 1
总结:
queuePriority
不能取代依赖关系
queuePriority
属性只对同一个队列有效
十.设置完成回调
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
// 任务代码
NSLog(@"1");
}];
// 设置任务1完成的回调block
[operation1 setCompletionBlock:^{
NSLog(@"任务1完成了");
}];
十一. 监听状态
- 使用KVO观察对operation状态的监听: isExcuting, isFinished, isCancelled.
- isExcuting 正在执行任务
- isFinished 任务完成
- isCancelled 任务取消执行
网友评论