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. 暂停和取消的区别就在于:暂停操作之后还可以恢复操作,继续向下执行;而 取消操作之后,所有的操作就清空了,无法再接着执行剩下的操作。