iOS多线程之NSOperation
可以将NSOperation理解为任务操作,将NSOperationQueue理解为操作队列。
NSOperation是基于Objective-C封装的一套管理与执行线程操作的类。这个类是一个抽象类,通常情况下,我们会使用NSInvocationOperation和NSBlockOperation这两个子类来进行多线程开发,当然我们也可以写继承于NSOperation的类,封装我们自己的操作类。
1、关于NSOperation基类的解析
NSOperation类是NSInvocationOperation和NSBlockOperation类的父类,其中封装了基础的任务操作行为。其主要属性如下:
/*激活操作任务*/
- (void)start;
/*取消任务的执行*/
- (void)cancel;
/*这个方法留给子类重写,子类可以在这个方法中实现具体的操作执行行为*/
- (void)main;
/*阻塞当前现在,直到操作完成*/
- (void)waitUntilFinished;
//一些常用属性
/*当前操作是否取消 */
@property (readonly, getter=isCancelled) BOOL cancelled;
/* 当前操作是否正在执行*/
@property (readonly, getter=isExecuting) BOOL executing;
/* 当前操作是否执行结束*/
@property (readonly, getter=isFinished) BOOL finished;
/* 当前操作是否在异步线程中*/
@property (readonly, getter=isAsynchronous) BOOL asynchronous;
/* 当前线程是否准备好*/
@property (readonly, getter=isReady) BOOL ready;
/* 设置在操作队列中的优先级*/
@property NSOperationQueuePriority queuePriority;
/* 设置操作执行完成后的回调*/
@property (nullable, copy) void (^completionBlock)(void);
/* 设置线程的优先级*/
@property double threadPriority;
/* 设置当前操作的名称*/
@property (nullable, copy) NSString *name;
2、NSBlockOperation类的应用
NSBlockOperation是NSOperation的一个子类,其可以异步执行多个Block代码块,当所有的Block都执行完成时,这个操作才算完成。
其常见方法属性如下:
/*初始化 */
+ (instancetype)blockOperationWithBlock:(void (^)(void))block;
/* 当对象初始化完成后,可以使用下面的方法来追加任务Block*/
- (void)addExecutionBlock:(void (^)(void))block;
/* 通过下面的属性可以获取NSBlockOperation对象中所追加的所有任务Block*/
@property (readonly, copy) NSArray<void (^)(void)> *executionBlocks;
关于其应用,示例如下:
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 5; i ++) {
NSLog(@"%@ = %d",[NSThread currentThread],i);
}
}];
[operation addExecutionBlock:^{
for (int i = 0; i < 5; i ++) {
NSLog(@"%@ = %d",[NSThread currentThread],i);
}
}];
[operation start];
打印信息:
image.png
从打印信息可以看出,两个for循环是在不同的线程中执行的,并且是异步执行。使用NSBlockOperation的一大好处是开发者不需要显示的操作NSThred来管理线程,NSBlockOperation会自动更具加入其中的任务Block来分配线程,使之异步执行。
3、NSInvocationOperation类的应用
NSInvocationOperation也是NSOperation的一个子类,和NSBlockOperation不同的是,这个类所执行的操作是在当前线程中同步执行的。其初始化方法如下:
/* 通过选择器来初始化对象*/
- (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;
/*通过NSInvocation来初始化对象 */
- (instancetype)initWithInvocation:(NSInvocation *)inv;
应用的示例代码:
- (void)creatInvocationOperation {
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(log) object:nil];
[operation start];
for (int i = 0; i < 5; i ++) {
NSLog(@"1-%@ = %d",[NSThread currentThread],i);
}
}
- (void)log {
for (int i = 0; i < 5; i ++) {
NSLog(@"2-%@ = %d",[NSThread currentThread],i);
}
}
打印信息如下:
image.png
从打印信息可以看出,NSInvocationOperation中的任务也是在主线程中执行的。
4、操作之间的依赖关系
操作之间常存在一些依赖关系。例如我们在开发应用时,经常会使用网络请求的数据渲染界面。其实请求操作与界面渲染操作就存在依赖关系,界面的渲染一定要依赖数据请求完成。依赖关系和优先级的作用很像,也有所不同。如果一个操作A依赖于另一个操作B,那么只有当B操作完成后,A操作才能执行。
/*操作添加依赖 */
- (void)addDependency:(NSOperation *)op;
/*删除一个依赖*/
- (void)removeDependency:(NSOperation *)op;
从原则上说,一个操作对象可以添加多个依赖,并且当所有依赖都执行完成后才会执行这个操作。
5、NSOperationQueue操作队列的应用
NSOperationQueue是操作队列类,通过上面的介绍,我们已经可以理解操作对象,并且操作对象默认的执行方式是串行的。如果将操作对象放入操作队列中,则默认为并行执行的。
示例代码如下:
- (void)creatOperationQueue {
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(log) object:nil];
[queue addOperation:operation1];
for (int i = 0; i < 5; i ++) {
NSLog(@"1-%@ = %d",[NSThread currentThread],i);
}
}
- (void)log {
for (int i = 0; i < 5; i ++) {
NSLog(@"2-%@ = %d",[NSThread currentThread],i);
}
}
打印信息:
image.png
从打印数据可以看出,队列的操作是在一个新的线程中执行的,并且操作队列之中的操作也都是在异步执行的。
NSOperationQuere类中相关的方法和属性如下:
/* 在队列中添加一个操作任务*/
- (void)addOperation:(NSOperation *)op;
/*在队里中插入一组操作任务,后面的参数设置是否在队列中的任务都执行完成后,再执行这一组操作 */
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait ;
/*在队列中添加一个block操作 */
- (void)addOperationWithBlock:(void (^)(void))block;
/*设置队列的最大并行操作数量 */
@property NSInteger maxConcurrentOperationCount;
/* 设置是否暂停队列任务执行*/
@property (getter=isSuspended) BOOL suspended;
/* 设置队列名*/
@property (nullable, copy) NSString *name ;
/*设置队列的优先级 */
@property NSQualityOfService qualityOfService;
/* 取消队列中的所有操作任务*/
- (void)cancelAllOperations;
/* 阻塞当前线程,知道队列中所有任务完成*/
- (void)waitUntilAllOperationsAreFinished;
/* 获取当前线程*/
@property (class, readonly, strong, nullable) NSOperationQueue *currentQueue;
/* 获取主线程中的操作队列*/
@property (class, readonly, strong) NSOperationQueue *mainQueue;
关于操作队列中操作的执行顺序,只要牢记下面的法则即可:
·决定于依赖关系,只有当这个操作的依赖全部执行完成后,它才会被执行。
·影响于优先级,优先级高的会先执行。
示例如下:
- (void)creatOperationDependency {
//创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//创建操作任务
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 5; i ++) {
NSLog(@"%@-%d:1",[NSThread currentThread],i);
sleep(0.5);
}
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 5; i ++) {
NSLog(@"%@-%d:2",[NSThread currentThread],i);
sleep(0.5);
}
}];
//添加依赖
[operation2 addDependency:operation1];
[queue addOperation:operation1];
[queue addOperation:operation2];
}
打印信息:
image.png
从打印信息我们可以看出,本来是两个并发异步的任务,在增加依赖关系后,只有当被依赖的任务全部完成后,才执行当前任务。