NSOperation介绍
NSOperation 是GCD 的封装,相比于GCD添加了额外的一些操作,比如取消任务、添加任务依赖关系。不需要关心线程生命周期。
NSOperation是一个抽象基类,使用比较多的就是它的子类NSInvocationOperation和NSBlockOperation。或者自定义NSOperation的子类。
NSOperation 常用属性和方法 | 解释 |
---|---|
dependencies | 在当前操作开始执行之前完成执行的所有操作对象数组 |
-(void)cancel | 可取消操作,实质是标记 isCancelled 状态 |
-(BOOL)isFinished | 判断操作是否已经结束 |
-(BOOL)isCancelled | 判断操作是否已经标记为取消 |
-(BOOL)isExecuting | 判断操作是否正在在运行 |
-(BOOL)isReady | 判断操作是否处于准备就绪状态,这个值和操作的依赖关系相关 |
-(void)waitUntilFinished | 阻塞当前线程,直到该操作结束。可用于线程执行顺序的同步 |
-(void)setCompletionBlock:(void (^)(void))block; completionBlock | 会在当前操作执行完毕时执行 completionBlock |
-(void)addDependency:(NSOperation *)op | 添加依赖,使当前操作依赖于操作 op 的完成 |
-(void)removeDependency:(NSOperation *)op | 移除依赖,取消当前操作对操作 op 的依赖 |
-(NSOperationQueuePriority)queuePriority | 获取优先级 |
-(void)setQueuePriority:(NSOperationQueuePriority)p | 设置优先级,详细见枚举NSOperationQueuePriority |
多线程实践
默认情况下方法是在当前线程执行。如果需要异步多线程执行,需要通过NSOperationQueue。
NSOperationQueue
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; //主队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init]; //自定义队列
[queue setMaxConcurrentOperationCount:1]//串行队列
[queue setMaxConcurrentOperationCount:4]//count数值大于1 为并发队列
//系统默认最大并发数-1,表示不限制。
[queue setSuspended:YES];// 暂停队列
BOOL res = [queue isSuspended];//判断队列是否暂停
//暂停和取消不是立刻取消当前操作,而是等当前的操作执行完之后不再进行新的操作。
NSOperationQueue属性和方法 | 解释 |
---|---|
+(id)currentQueue | 获取当前队列,如果当前线程不是在 NSOperationQueue 上运行则返回 nil |
+(id)mainQueue | 获取主队列 |
-(void)cancelAllOperations | 可以取消队列的所有操作 |
-(BOOL)isSuspended | 判断队列是否处于暂停状态。 YES 为暂停状态,NO 为恢复状态 |
-(void)setSuspended:(BOOL)b | 可设置操作的暂停和恢复,YES 代表暂停队列,NO 代表恢复队列 |
-(void)waitUntilAllOperationsAreFinished | 阻塞当前线程,直到队列中的操作全部执行完毕 |
-(void)addOperationWithBlock:(void (^)(void))block | 向队列中添加一个 NSBlockOperation 类型操作对象 |
-(void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait | 向队列中添加操作数组,wait 标志是否阻塞当前线程直到所有操作结束 |
-(NSArray *)operations | 当前在队列中的操作数组(某个操作执行结束后会自动从这个数组清除 |
-(NSUInteger)operationCount | 当前队列中的操作数 |
P.s.
- 这里的暂停和取消(包括操作的取消和队列的取消)并不代表可以将当前的操作立即取消,而是当当前的操作执行完毕之后不再执行新的操作。
- 暂停和取消的区别就在于:暂停操作之后还可以恢复操作,继续向下执行;而取消操作之后,所有的操作就清空了,无法再接着执行剩下的操作。
- 你不能重用一个操作对象。一旦它被添加到一个队列中,你就丧失了对它的所有权。如果你想再使用同一个操作类,就必须创建一个新的变量。一个结束的操作不能被重启。
NSInvocationOperation
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(doSth) object:nil];
NSLog(@"%@",[NSThread currentThread]);
[operation start];
- (void)doSth{
NSLog(@"%@",[NSThread currentThread]);
}
开启新线程:
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(doSth) object:nil];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//设置队列运行线程数
[queue setMaxConcurrentOperationCount:4]
[queue addOperation:operation];
NSLog(@"%@",[NSThread currentThread]);
- (void)doSth{
NSLog(@"%@",[NSThread currentThread]);
}
NSBlockOperation
可以通过设置completionBlock监听操作的完成与否。
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@",[NSThread currentThread]);
}];
operation.completionBlock = ^{
// 操作完成后的操作
NSLog(@"NSBlockOperation%@", [NSThread currentThread]);
};
[operation start];
单独执行一个block是在当前线程中执行,向NSBlockOperation中加入多个block,会进行异步多线程执行操作。
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"%@",[NSThread currentThread]);
}];
[operation start];
也可以将operation加入到队列中来实现多线程操作。
NSBlockOperation *operation = [[NSBlockOperation alloc]init];
[operation addExecutionBlock:^{
NSLog(@"%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"%@",[NSThread currentThread]);
}];
NSOperationQueue *queue = [NSOperationQueue new];
//设置队列运行线程数
[queue setMaxConcurrentOperationCount:4]
[queue addOperation:operation];
NSOperation 操作依赖
通过操作依赖,我们可以很方便的控制操作之间的执行先后顺序。注意不能互相依赖。一个操作可以依赖其他多个操作。
可以在不同queue的NSOperation之间添加依赖。依赖高于优先级priority。
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"操作1");
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"操作2");
}];
[op1 addDependency:op2];
//NSLog(@"%@",[op1 dependencies]);//获取操作的依赖集合
//[op1 removeDependency:op2];//移除依赖
[queue addOperation:op1];
[queue addOperation:op2];
操作结果:
2019-11-15 11:52:13.983959+0800 iosTest[31796:1810100] 操作2
2019-11-15 11:52:13.984296+0800 iosTest[31796:1810101] 操作1
NSOperation 返回主线程
- (void)communication {
//创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//添加操作
[queue addOperationWithBlock:^{
// 异步进行耗时操作
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@", [NSThread currentThread]);
}
// 回到主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
}
}];
}];
}
自定义 NSOperation子类
ZJOperation.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class ZJOperation;
@protocol ZJOperationDelegate <NSObject>
@optional
- (void)operation:(ZJOperation *)operation withObj:(id)obj;
@end
@interface ZJOperation : NSOperation
@property (nonatomic, weak) id<ZJOperationDelegate> delegate;
@end
NS_ASSUME_NONNULL_END
ZJOperation.m
#import "ZJOperation.h"
@implementation ZJOperation
- (void)main{
//不能访问主线程的自动释放池,所以应该手动创建一个。
@autoreleasepool {
if(self.isCancelled)return;
//模拟耗时操作
if ([self.delegate respondsToSelector:@selector(operation:withObj:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
// 返回主线程,传递数据给代理对象
[self.delegate operation:self withObj:nil];
});
}
}
}
@end
P.s.
如果需要重写start方法,需要手动管理状态有:
isExecuting 代表任务正在执行中。isFinished 代表任务已经执行完成。isCancelled 代表任务已经取消执行。
通过KVO监控NSOperation对象的属性:可以通过isCancelled属性来判断任务是否已取消,通过isFinished属性来判断任务是否已经完成等等。