NSOperation
特点
NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对
象。但是比 GCD 更简单易用、代码可读性也更高。
1. 可添加完成的代码块,在操作完成后执行。
2. 添加操作之间的【依赖关系】,方便的控制执行顺序。
注意:依赖使用要注意优先级关系,依赖+优先级任务 可能导致优先级反转,甚
至死锁
3. 设定操作执行的【优先级】。
4. 可以很方便的【取消一个操作】的执行。
5. 使用 KVO 【观察对操】作执行状态的更改:isExecuteing、isFinished、
isCancelled。
6.操作队列通过设置 **最大并发操作数(maxConcurrentOperationCount)**
来控制并发、串行
NSOperationQueue:
队列,或者说是一个线程池功能
NSOperation 实现多线程步骤:
1. 创建操作:先将需要执行的操作封装到一个 NSOperation 对象中。
2. 创建队列:创建 NSOperationQueue 对象。
3. 将操作加入到队列中:将 NSOperation 对象添加到 NSOperationQueue 对
象中。
4.之后将 NSOperationQueue 中的 NSOperation 取出来,在新线程中执行操作
GCD 内部也是维护一个线程池,内部自动管理线程
NSOperation 创建
下面代码如果只是简单使用NSOperation,那么是在当前线程执行的,不会开启
子线程:
// 1.创建 NSOperation 对象
NSOperation *op = [[NSOperation alloc] init];
// 2.调用 start 方法开始执行操作
[op start];
注意:
NSOperation 需要配合 NSOperationQueue 来实现多线程
NSBlockOperation
可以使用子类 NSBlockOperation 操作异步线程任务
示例1
blockOperationWithBlock 方法会根据任务自动安排异步线程执行:
// ️ 1.创建 NSBlockOperation 对象
NSBlockOperation *op =
[NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
}
}];
// ️ 2.调用 start 方法开始执行操作
[op start];
疑问?
如果上面代码在 start 之前,我又想再添加异步任务呢?
使用 addExecutionBlock:
示例2
// 1.创建 NSBlockOperation 对象
NSBlockOperation *op = [NSBlockOperation
blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
}
}];
// ️ 2.添加额外的操作任务
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@", [NSThread currentThread]); // 打印当前线程
}
}];
// 3.调用 start 方法开始执行操作
[op start];
会异步执行这些任务
继承 NSOperation
可以通过重写 `main` 或者 `start` 方法 来定义自己的 NSOperation 对象
@interface KKCOperation : NSOperation
@end
@implementation KKCOperation
- (void)main {
if (!self.isCancelled) {
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@", [NSThread currentThread]);
}
}
}
@end
这样,调用start 开启NSOperation 时,就会调用main函数里面方法,或者重写
start也行
NSOperationQueue
// 主队列获取方法(主线程中执行,串行)
NSOperationQueue *queue = [NSOperationQueue mainQueue];
// 自定义队列创建方法
串行:【最大并发数=1】或者
并发 : 最大并发数 != 1
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSOperationQueue 操作异步任务主要有两种方式:
1. 直接将NSOperation 或者其子类对象直接添加
- (void)addOperation:(NSOperation *)op;
2, 直接将任务以block形式添加到队列
- (void)addOperationWithBlock:(void (^)(void))block;
1 添加Operation形式
- (void)addOperationToQueue {
// 1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.1 使用 NSInvocationOperation 创建操作1
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]
initWithTarget:self selector:@selector(task1) object:nil];
// 2.2 使用 NSBlockOperation 创建操作3
NSBlockOperation *op2 = [NSBlockOperation
blockOperationWithBlock:^{
// 任务2
}];
// 添加任务方式2
[op3 addExecutionBlock:^{
// 任务3
}];
// 3.使用 addOperation: 添加所有操作到队列中
[queue addOperation:op1]; // [op1 start]
[queue addOperation:op2]; // [op2 start]
}
- (void)task1 {
// 任务1
}
任务并发执行(默认最大并发数-1.不限制)
2,直接添加block任务
// 1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.使用 addOperationWithBlock: 添加操作到队列中
[queue addOperationWithBlock:^{
// 耗时任务1
}];
[queue addOperationWithBlock:^{
// 耗时任务2
}];
任务并发执行(默认最大并发数-1.不限制)
并发数
如果上面的代码设置并发数=1:
queue.maxConcurrentOperationCount = 1;
那就不再是并发执行,而是串行
操作依赖
操作
addDependency: 添加依赖
removeDependency:移除依赖
在当前操作开始执行之前完成执行的所有操作对象数组。
@property (readonly, copy) NSArray<NSOperation *> *dependencies;
示例
- (void)addDependency {
// 1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.创建操作
NSBlockOperation *op1 = [NSBlockOperation
blockOperationWithBlock:^{
// 耗时任务1
}];
NSBlockOperation *op2 = [NSBlockOperation
blockOperationWithBlock:^{
// 耗时任务2
}];
// 3.添加依赖,先执行op1,再执行op2
[op2 addDependency:op1];
// 4.添加操作到队列中
[queue addOperation:op1];
[queue addOperation:op2];
}
优先级
准备就绪的任务: 不需要依赖其它任务,可以执行的状态
互不依赖的两个任务 A,B。异步执行,先执行优先级高的任务
任务A依赖任务B,任务A优先级高。 结果是先执行任务B,优先处理依赖关系,
而不得优先级
线程间通讯
子线程处理耗时,回主线程刷新UI
// 1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
// 2.添加操作
[queue addOperationWithBlock:^{
// 耗时任务1
// 回到主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// UI 刷新等操作
}];
}];
常用属性和方法
NSOperation
1. 取消操作方法
- `- (void)cancel;` 可取消操作,实质是标记 isCancelled 状态。
2. 判断操作状态方法
- `- (BOOL)isFinished;` 判断操作是否已经结束。
- `- (BOOL)isCancelled;` 判断操作是否已经标记为取消。
- `- (BOOL)isExecuting;` 判断操作是否正在在运行。
- `- (BOOL)isReady;` 判断操作是否处于准备就绪状态,这个值和操作的依
赖关系相关。
3. 操作同步
- `- (void)waitUntilFinished;` 阻塞当前线程,直到该操作结束。可用于线
程执行顺序的同步。
- `- (void)setCompletionBlock:(void (^)(void))block;`
`completionBlock` 会在当前操作执行完毕时执行 completionBlock。
- `- (void)addDependency:(NSOperation *)op;` 添加依赖,使当前操作依
赖于操作 op 的完成。
- `- (void)removeDependency:(NSOperation *)op;` 移除依赖,取消当前
操作对操作 op 的依赖。
- `@property (readonly, copy) NSArray<NSOperation *>
*dependencies;` 在当前操作开始执行之前完成执行的所有操作对象数组。
NSOperationQueue
1. 取消/暂停/恢复操作
- `- (void)cancelAllOperations;` 可以取消队列的所有操作。
- `- (BOOL)isSuspended;` 判断队列是否处于暂停状态。 YES 为暂停状态,
NO 为恢复状态。
- `- (void)setSuspended:(BOOL)b; `可设置操作的暂停和恢复,YES 代表暂
停队列,NO 代表恢复队列。
2. 操作同步
- `- (void)waitUntilAllOperationsAreFinished;` 阻塞当前线程,直到队列
中的操作全部执行完毕。
3. 添加/获取操作
- `- (void)addOperationWithBlock:(void (^)(void))block;` 向队列中添加一
个 NSBlockOperation 类型操作对象。
- `- (void)addOperations:(NSArray *)ops waitUntilFinished:
(BOOL)wait;` 向队列中添加操作数组,wait 标志是否阻塞当前线程直到所有操
作结束
- `- (NSArray *)operations;` 当前在队列中的操作数组(某个操作执行结束
后会自动从这个数组清除)。
- `- (NSUInteger)operationCount;` 当前队列中的操作数。
4. 获取队列
- `+ (id)currentQueue;` 获取当前队列,如果当前线程不是在
NSOperationQueue 上运行则返回 nil。
- `+ (id)mainQueue;` 获取主队列。
注意:
1. 这里的暂停和取消(包括操作的取消和队列的取消)并不代表可以将当前的操
作立即取消,而是当当前的操作执行完毕之后不再执行新的操作。
2. 暂停和取消的区别就在于:暂停操作之后还可以恢复操作,继续向下执行;而
取消操作之后,所有的操作就清空了,无法再接着执行剩下的操作。