NSOperation在iOS 4后也基于GCD实现,但是相对于GCD来说可控性更强,并且可以加入操作依赖。NSOperation是一个抽象类,因此系统提供了NSBlockOperation和NSInvocationOperation两个子类,并且可以创建继承自NSOperation的自定义类。相比于GCD,NSOperation更加面向对象,开发者除了不需要去了解线程相关的概念之外,甚至连GCD中需要了解的异步/同步、并行/串行都不太需要深入了解,开发者只要懂得任务和队列即可。
NSBlockOperation类提供了如下操作方法,用于创建一个NSBlockOperation对象,任务封装在Block中。
+(instancetype)blockOperationWithBlock:(void (^)(void))block;
// 对于NSInvocationOperation类来说,需要执行的任务直接指定已定义的方法。
-(nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)tag;
2.无队列情况下执行任务
在不创建队列的情况下,可以直接调用NSOperation类的start方法来执行某个任务。
该任务是在当前线程进行执行的,如果是在主线程调用的start方法,那么则会在主线程中执行任务。
// 示例
-(void)executeInCurrentThread {
NSBlockOperation *task1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"");
}];
NSBlockOperation *task2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"");
}];
// 调用start方法,会在当前线程中串行执行
[task1 start];
[task1 start];
}
3.在队列中执行任务
当把任务放到队列(NSOperationQueue类)中进行执行时,系统会自动并行执行所有任务,
对于开多少条线程之类的事务,完全交由系统处理,开发者只要把任务添加到队列中即可。
另外,可以通过设置队列的maxConcurrentOperationCount属性,来设置并行执行任务的数量。
-(void)executeInQueue {
NSBlockOperation *task1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"");
}];
NSBlockOperation *task2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"");
}];
NSBlockOperation *task3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"");
}];
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 设置队列属性
queue.maxConcurrentOperationCount = 3;
// 添加任务到队列
[queue addOperation:task1];
[queue addOperation:task2];
[queue addOperation:task3];
}
4.在任务中添加新任务
通过调用NSOperation类的addExecutionBlock方法,可以为某个NSOperation对象增加额外的任务。
当把这些新增的任务放到队列中执行时,也是并行执行的。
-(void)addExecutionBlock:(void (^)(void))block;
// 示例 在任务中添加新任务,所有的任务都会并行执行。
-(void)addTaskInOperation {
NSBlockOperation *task1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task1---");
}];
// task1中添加task
[task1 addExecutionBlock:^{
NSLog(@"task1 add task ");
}];
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 添加任务到队列
[queue addOperation:task1];
}
5.在队列中直接添加任务
-(void) addTaskInQueue {
NSBlockOperation *task1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task1---");
}];
// task1中添加task
[task1 addExecutionBlock:^{
NSLog(@"task1 add task ");
}];
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 添加任务到队列
[queue addOperation:task1];
// 在queue中添加任务
[queue addOperationWithBlock:^{
NSLog(@"queue task --");
}];
}
6.在任务中创建completionBlock
在NSOperation类中,可以通过设置completionBlock来创建所有任务执行完成后,自动调用的一个Block。
该Block的执行是在任务执行后被调用的,有先后顺序。
-(void)addCompletionBlock {
NSBlockOperation *task1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task1---");
}];
task1.completionBlock = ^{
NSLog(@"task1 end ---");
};
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:task1];
}
NSOperation与GCD的区别
GCD是底层的C语言构成的API,而NSOperationQueue及相关对象是Objective-C的对象。在GCD中,在队列中执行的是由Block构成的任务,这是一个轻量级的数据结构;而NSOperation作为一个对象,提供了更多的选择。
在NSOperationQueue中,可以随时取消已经设定要准备执行的任务(当然,已经开始的任务就无法阻止了),而GCD没法停止已经加入queue的Block(其实是有的,但需要许多复杂的代码)。
NSOperation能够方便地设置依赖关系,可以让一个Operation依赖于另一个Operation,这样的话尽管两个Operation处于同一个并行队列中,但前者会直到后者执行完毕后再执行。
KVO可以应用在NSOperation中,可以监听一个Operation是否完成或取消,这样能比GCD更加有效地掌控执行的后台任务。
在NSOperation中,能够设置NSOperation的priority优先级,能够使同一个并行队列中的任务区分先后地执行。而在GCD中,只能区分不同任务队列的优先级,如果要区分Block任务的优先级,也需要大量的复杂代码。
用户能够对NSOperation进行继承,在这之上添加成员变量与成员方法,提高整个代码的复用度,这比简单地将Block任务排入执行队列更有自由度,能够在其之上添加更多自定制的功能。
线程间通信
// 示例
-(IBAction)downLoadImage:(id)sender {
// 创建任务
NSBlockOperation *downloadTask = [NSBlockOperation blockOperationWithBlock:^{
// ...todo
// 返回主线程 设置UI
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperationWithBlock:^{
//
}]
}];
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:downLoadTask];
}
在GCD中可以使用队列组来设置任务之间的依赖关系,而在NSOperation中则提供了更加方便直观的方式来设置任务执行的先后顺序关系。通过NSOperation类中的addDependency方法,即可添加任务之间的依赖关系。由于addDependency是NSOperation类中的方法,与队列无关,因此也可以针对不同队列中的任务设置任务执行的先后依赖关系。
// 示例
-(IBAction)downLoadTwoImage:(id)sender {
// 创建任务
NSBlockOperation *task1= [NSBlockOperation blockOperationWithBlock:^{
// ...todo
// 返回主线程 设置UI
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperationWithBlock:^{
//
}]
}];
NSBlockOperation *task2= [NSBlockOperation blockOperationWithBlock:^{
// ...todo
// 返回主线程 设置UI
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperationWithBlock:^{
//
}]
}];
NSBlockOperation *task3= [NSBlockOperation blockOperationWithBlock:^{
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperationWithBlock:^{
//
}]
}];
// 设置任务之间依赖关系
[task3 addDependency:task1];
[task3 addDependency:task2];
[task2 addDependency:task1];
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:task1];
[queue addOperation:task2];
[queue addOperation:task3];
}