NSOperation:1-基本使用
NSOperation在iOS4后也基于GCD实现,但是相对于GCD来说可控性更强,并且可以加入操作依赖。NSOperation是一个抽象类,因此系统为我们提供了NSBlockOperation和NSInvocationOperation两个子类,并且可以创建继承自NSOperation的自定义类。相比于GCD,NSOperation更加面向对象,开发者除了不需要去了解线程相关的概念之外,甚至连GCD中需要了解的异步/同步、并行/串行都不太需要深入了解,开发者只要懂得任务和队列即可。
NSOperation的类族
由于NSOperation是一个抽象类,因此不能够直接使用NSOperation,但苹果提供了两个NSOperation的子类,NSBlockOperation和NSInvocationOperation,除此之外,我们还可以使用自定义CustomOperation类。
对于NSBlockOperation类来讲,可以把任务封装在一个block块之内。NSBlockOperation类提供了如下操作方法。
- 创建一个NSBlockOperation对象,任务封装在block中
+ (instancetype)blockOperationWithBlock:(void (^)(void))block;
对于NSInvocationOperation类来说,需要执行的任务直接指定已定义的方法。
- (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;
无队列情况下执行任务
在不创建队列的情况下,可以直接调用NSOperation类的start方法来执行某个任务。该任务是在当前线程进行执行的,如果是在主线程调用的start方法,那么则会在主线程中执行任务。
下方的示例代码中,在当前线程中串行执行两个任务。
/*
调用operation的start方法,在当前线程中串行执行,无队列
*/
-(void) executeInCurrentThread {
NSBlockOperation *task1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task1-----%@", [NSThread currentThread]);
}];
NSBlockOperation *task2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task2-----%@", [NSThread currentThread]);
}];
//调用start方法,会在当前线程中串行执行
[task1 start];
[task2 start];
}
运行结果如下。
在队列中执行任务
当把任务放到队列(NSOperationQueue类)中进行执行时,系统会自动并行执行所有任务,对于开多少条线程之类的事务,完全交由系统处理,开发者只要把任务添加到队列中即可。另外,可以通过设置队列的maxConcurrentOperationCount属性,来设置并行执行任务的数量。
下方的示例代码中,在一个队列中插入了5个任务,这5个任务并行执行。系统会同时开启多个线程,多个任务并行执行。另外,maxConcurrentOperationCount决定了“并发”任务的数量,而不是创建线程的数量。即便设置为1,不同的任务也有可能在不同的线程中执行
-(void) executeInQueue {
NSBlockOperation *task1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task1-----%@", [NSThread currentThread]);
}];
NSBlockOperation *task2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task2-----%@", [NSThread currentThread]);
}];
NSBlockOperation *task3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task3-----%@", [NSThread currentThread]);
}];
NSBlockOperation *task4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task4-----%@", [NSThread currentThread]);
}];
NSBlockOperation *task5 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task5-----%@", [NSThread currentThread]);
}];
//创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//设置队列属性
queue.maxConcurrentOperationCount = 5;
//添加任务到队列
[queue addOperation:task1];
[queue addOperation:task2];
[queue addOperation:task3];
[queue addOperation:task4];
[queue addOperation:task5];
}
运行结果如下。可见系统同时执行5个任务,执行的顺序是不确定的,并且创建了5条线程。
在任务中添加新任务
通过调用NSOperation类的addExecutionBlock方法,可以为某个NSOperation对象增加额外的任务。当把这些新增的任务放到队列中执行时,也是并行执行的。
- (void)addExecutionBlock:(void (^)(void))block;
下方的示例代码中在任务中添加新任务,所有的任务都会并行执行。
-(void) addTaskInOperation {
NSBlockOperation *task1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task1-----%@", [NSThread currentThread]);
}];
NSBlockOperation *task2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task2-----%@", [NSThread currentThread]);
}];
//task1中添加task
[task1 addExecutionBlock:^{
NSLog(@"task1 add task-----%@", [NSThread currentThread]);
}];
//task2中添加task
[task2 addExecutionBlock:^{
NSLog(@"task2 add task-----%@", [NSThread currentThread]);
}];
//创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//添加任务到队列
[queue addOperation:task1];
[queue addOperation:task2];
}
运行结果如下。
在队列中直接添加任务
通过调用NSOperationQueue的addOperationWithBlock方法,可以向队列中直接添加任务block块。
-(void) addTaskInQueue {
NSBlockOperation *task1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task1-----%@", [NSThread currentThread]);
}];
NSBlockOperation *task2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task2-----%@", [NSThread currentThread]);
}];
//task1中添加task
[task1 addExecutionBlock:^{
NSLog(@"task1 add task-----%@", [NSThread currentThread]);
}];
//task2中添加task
[task2 addExecutionBlock:^{
NSLog(@"task2 add task-----%@", [NSThread currentThread]);
}];
//创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//添加任务到队列
[queue addOperation:task1];
[queue addOperation:task2];
//在queue中添加任务
[queue addOperationWithBlock:^{
NSLog(@"queue task-----%@", [NSThread currentThread]);
}];
}
运行结果如下。向队列queue中直接添加任务,也是并行执行的。
在任务中创建completionBlock
在NSOperation类中,可以通过设置completionBlock来创建所有任务执行完成后,自动调用的一个block块。该block的执行是在任务执行后被调用的,有先后顺序。
/*
可以给任务Operation结束后添加block,但是block中的任务会在新的线程中执行,即:仅仅是添加了一个任务执行的先后顺序关系
*/
-(void) addCompletionBlock {
NSBlockOperation *task1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task1-----%@", [NSThread currentThread]);
}];
NSBlockOperation *task2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task2-----%@", [NSThread currentThread]);
}];
//task1中添加task
[task1 addExecutionBlock:^{
NSLog(@"task1 add task-----%@", [NSThread currentThread]);
}];
task1.completionBlock = ^{
NSLog(@"task1 end!!! %@", [NSThread currentThread]);
};
//task2中添加task
[task2 addExecutionBlock:^{
NSLog(@"task2 add task-----%@", [NSThread currentThread]);
}];
task2.completionBlock = ^{
NSLog(@"task2 end!!! %@", [NSThread currentThread]);
};
//创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//添加任务到队列
[queue addOperation:task1];
[queue addOperation:task2];
}
运行结果如下。
NSOperation与GCD的区别
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任务排入执行队列更有自由度,能够在其之上添加更多自定制的功能。
示例代码
https://github.com/99ios/11.3.1
最后编辑时间为: September 15th , 2017 at 05:02 am
本文由 99ios 创作,转载请注明出处