NSOperation
是一个抽象类,可以用来封装一个任务的相关代码和数据。因为这是个抽象类,所以不能直接得到该类的实例化对象,而是需要继承该类实现其子类或者使用系统内置的两个子类(NSInvocationOperation
和NSBlockOperation
)来执行实际的线程任务。
一个Operation对象包含的任务只能执行一次。通常情况下只需要把Operation对象加入Operation queue
(它是NSOperationQueue
类的一个实例)即可。一个Operation queue
可以直接执行Operation,或者开第二个线程运行Operation,甚至是间接的使用libdispatch
库(类似GCD)。
如果不使用Operation queue
,也可以通过调用其start
方法执行一个Operation。手动启动Operation会相对困难一点,因为一不小心启动一个不处于准备状态(ready trigger)
的Operation将会导致异常发生。
在这里我们不打算对NSOperation进行过多的说明。我们主要集中于介绍如何使用系统提供的NSInvocationOperation
,NSBlockOperation
和NSOperationQueue
来进行多线程编程。
简单入门
使用Operation进行多线程编程的步骤很简单。
- 实例化NSOperation子类实例,绑定要执行的操作。
- 创建NSOperationQueue队列,并将NSOperation实例添加到队列中。
然后NSOperationQueue队列将会自动检测队列中需要执行的Operation对象并执行它们。所有使用非常简单,还不需要对Operation的生命周期进行维护。
在样例代码中首先创建了一个NSOperationQueue类型的属性,并且重写init方法对齐进行初始化操作。部分代码如下
@property (nonatomic, strong) NSOperationQueue *queue;
//重写init方法初始化该属性
-(instancetype)init
{
self = [super init];
if (self) {
_queue = [[NSOperationQueue alloc] init];
}
return self;
}
NSInvocationOperation
创建NSInvocationOperation主要有两个方法。
方法一
//方法一,创建执行特定方法的Operation
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(selector) object:nil];
[self.queue addOperation:operation];
方法二
对于NSInvocation,本质上是OC消息的一种静态描述,可以将其看成是把一个动作(action)封装到一个对象中。一个NSInvocation对象包含了OC消息的所有元素:目标(target),选择器(selector),参数(arguement),返回值(return value)。每个元素都是可以设置的,并且返回值会在齐被调度后自动设置相应的值。
//方法二,创建NSInvocation对象,并设置其相应参数,在用于创建NSInvocationOperation实例
NSInvocation *invocation =
[NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(run)]];
//设置selector属性
[invocation setSelector:@selector(run)];
//设置target属性
[invocation setTarget:self];
//创建实例
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithInvocation:invocation];
[self.queue addOperation:operation2];
NSBlockOperation
NSBlockOperation是NSOperation的一个子类之一。其创建方式比NSInvocationOperation看起来要更简便一点。
//创建实例
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 100; i++) {
NSLog(@"%zd ===> %@", i, [NSThread currentThread]);
}
}];
//添加到队列中
[self.queue addOperation:operation];
自定义NSOperation子类
自定义NSOperation子类和自定义NSThread子类的方式非常的像,都是继承父类,重写它们的main方法执行特定的任务。
LYSOperation.h
#import <Foundation/Foundation.h>
@interface LYSOperation : NSOperation
@end
LYSOperation.m
/**
* 重写main方法,里面是Operation被调度时执行的任务
*/
-(void)main
{
NSLog(@"这里是自定义的NSOperation子类LYSOperation的实现.");
}
然后我们想使用别的NSOperation子类一样,只要将其加入NSOperationQueue队列中即可。
LYSOperation *operation = [[LYSOperation alloc] init];
[self.queue addOperation:operation];
operation间依赖关系
NSOperation还有一个特性就是可以创建Operation之间的依赖关系。就是如果A依赖于B,则A需要在B执行完之后才会执行。这样我们就可以很方便拥有特定执行顺序的任务集了。但是需要注意的是要避免创建依赖循环!
NSBlockOperation *operationA = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"以来测试 ===> 任务A");
}];
NSBlockOperation *operationB = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"以来测试 ===> 任务B");
}];
NSBlockOperation *operationC = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"以来测试 ===> 任务C");
}];
//设置A依赖于B,B依赖于C
[operationA addDependency:operationB];
[operationB addDependency:operationC];
//添加Operation到队列中
[self.queue addOperations:@[operationA, operationB, operationC] waitUntilFinished:NO];
现在我们来看一下结果。首先我们看一下不添加依赖时候的结果。我们会发现三个任务之间多次执行之后可能会出现任何的执行顺序。
2016-02-23 21:50:43.255 ThreadDemon[7245:589407] 依赖测试 ===> 任务A
2016-02-23 21:50:43.255 ThreadDemon[7245:589410] 依赖测试 ===> 任务C
2016-02-23 21:50:43.255 ThreadDemon[7245:589409] 依赖测试 ===> 任务B
我们再来看看添加了以来之后的执行结果。我们会发现无论运行多少次肯定都是先执行C,再执行B,最后执行A任务,这样依赖关系就可以得到体现了。
2016-02-23 21:52:59.482 ThreadDemon[7261:591072] 依赖测试 ===> 任务C
2016-02-23 21:52:59.483 ThreadDemon[7261:591070] 依赖测试 ===> 任务B
2016-02-23 21:52:59.483 ThreadDemon[7261:591070] 依赖测试 ===> 任务A
NSOperationQueue
这里不对NSOperation进行过多的深入介绍,主要是简单介绍几个常用的方法。
设置最大并发线程数
NSOperationQueue提供了方法设置最大可以并发执行的Operation数量。
[_queue setMaxConcurrentOperationCount:5];
获取当前执行Operation的Queue
NSOperationQueue *queue = [NSOperationQueue currentQueue];
获取绑定到主线程上的NSOperationQueue对象
NSOperationQueue *queue = [NSOperationQueue mainQueue];
Demon源代码
这里给出Github项目上的源代码。
传送门:Github-iOS_Framework_study-ThreadDemon