NSOpreation、NSOperationQueue简介
- NSOperation、NSOperationQueue 是苹果提供给我们的一套多线程解决方案。
- NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。
- 虽然是基于GCD封装,但是比 GCD 更简单易用、代码可读性也更高。
- GCD 中的一些概念同样适用于 NSOperation、NSOperationQueue。在 NSOperation、NSOperationQueue 中也有类似的任务(操作)和队列(操作队列)的概念。
- 在 NSOperation 中,由于NSOperation是一种抽象类,所以在使用过程中,我们需要用NSOperation的子类 NSInvocationOperation、NSBlockOperation。
啥是抽象类?
抽象类是不完整的,它只能用作基类。在面向对象方法中,抽象类主要用来进行类型隐藏和充当全局变量的角色。
来自百度百科
NSOpreation、NSOperationQueue使用
创建队列NSOperationQueue
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
创建操作NSOpreation
- NSInvocationOperation
NSInvocationOperation *io = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(InvocationOperationSelector) object:nil];
[io start];
-(void)InvocationOperationSelector{
NSLog(@"这是NSInvocationOperation执行的任务 %@",[NSThread currentThread]);
}
输出结果
image.png
可知,在操作没有添加到队列中,操作直接start,任务是在主线程上执行,没有开启线程。
- NSBlockOperation
NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"这是NSBlockOperation执行的任务 %@",[NSThread currentThread]);
}];
[bo start];
输出结果
从结果来看,跟
NSInvocationOperation
同理。尝试将操作添加到队列中看看输出结果。
- NSBlockOperation添加额外的任务
NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"这是NSBlockOperation执行的任务 %@",[NSThread currentThread]);
}];
[bo addExecutionBlock:^{
NSLog(@"这是让NSBlockOperation另外执行的任务 %@",[NSThread currentThread]);
}];
[bo addExecutionBlock:^{
NSLog(@"这是让NSBlockOperation另外执行的任务2 %@",[NSThread currentThread]);
}];
[bo start];
输出结果
通过输出结果可知,给操作添加额外任务,并不会让这些任务按顺序执行。但是初始化的任务仍然在主线程执行,另外两个任务分别创建了线程。
所以
NSBlockOperation
是否创建线程,取决于当前需要执行的任务数。那么,
blockOperationWithBlock
这里面的任务就一定会在主线程执行吗?我们给上面的代码加点戏,再看看打印结果。
NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"这是NSBlockOperation执行的任务 %@",[NSThread currentThread]);
}
}];
[bo addExecutionBlock:^{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"这是让NSBlockOperation另外执行的任务 %@",[NSThread currentThread]);
}
}];
[bo addExecutionBlock:^{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"这是让NSBlockOperation另外执行的任务2 %@",[NSThread currentThread]);
}
}];
[bo start];
打印结果
细心的小朋友已经发现了,
blockOperationWithBlock
里面的任务,并没有在主线程执行啦。
所以,总结一下
1.NSBlockOperation
是否创建线程,取决于当前需要执行的任务数。
2.blockOperationWithBlock
里面的任务,并没有不一定主线程执行啦。
添加操作(NSOpreation)到队列(NSOperationQueue)
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSInvocationOperation *io = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(InvocationOperationSelector) object:nil];
NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"这是NSBlockOperation执行的任务 %@",[NSThread currentThread]);
}
}];
[queue addOperation:io];
[queue addOperation:bo];
输出结果
image.png
可见,两个操作都分别创建了number为3和4的线程,不再是主线程。
注意:start
不能跟addOperation
同时使用,否则会导致崩溃
添加、移除依赖
- (void)addDependency:(NSOperation *)op;
添加依赖,使当前操作依赖于操作 op 的完成。
- (void)removeDependency:(NSOperation *)op;
移除依赖,取消当前操作对操作 op 的依赖。
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"这是bo1执行的任务 %@",[NSThread currentThread]);
}];
NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"这是bo2执行的任务 %@",[NSThread currentThread]);
}];
NSBlockOperation *bo3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"这是bo3执行的任务 %@",[NSThread currentThread]);
}];
//bo1依赖于bo2
[bo1 addDependency:bo2];
//bo2依赖于bo3
[bo2 addDependency:bo3];
[queue addOperations:@[bo1,bo2,bo3] waitUntilFinished:YES];
NSLog(@"如果上面waitUntilFinished是YES,我就会最后执行");
输出结果
waitUntilFinished
这里面传入的BOOL 值将会影响下面的代码执行顺序,如果是YES,将会形成一个类似同步函数、栅栏函数的同步功能。等队列中的任务中都执行结束后才执行下面的代码。
NSOperationQueue控制最大并发数(实现同步)
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
queue.maxConcurrentOperationCount = 1;
NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i<5; i++) {
NSLog(@"第%d次 执行任务 %@",i+1,[NSThread currentThread]);
}
}];
[bo addExecutionBlock:^{
for (int i = 0; i<5; i++) {
NSLog(@"第%d次 执行额外任务 %@",i+1,[NSThread currentThread]);
}
}];
[queue addOperation:bo];
先看输出结果
从输出结果可以看出来一些东西
1.任务并不是先执行
任务
后执行额外任务
,而是相互穿插。2.虽然跟理想中的结果不太一样,但是不论
任务
跟额外任务
,却按照12345的顺序,老老实实的执行。所以
这里
maxConcurrentOperationCount
控制的不是并发线程的数量,而是一个队列中同时能并发执行任务的最大数。而且一个任务也并非只能在一个线程中运行。
线程间通讯
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(10, 100, 200, 10)];
[self.view addSubview:label];
NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{
NSString * str = @"字符串赋值";
NSLog(@"这是子线程%@",[NSThread currentThread]);
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
NSLog(@"回到主线程刷新UI%@",[NSThread currentThread]);
label.text = str;
}];
}];
[queue addOperation:bo];
打印以及显示结果
image.png
NSOperation、NSOperationQueue 常用属性和方法归纳
NSOperation 常用属性和方法
1.取消操作方法
- (void)cancel;
可取消操作,实质是标记 isCancelled 状态。
2.判断操作状态方法
- (BOOL)isFinished;
判断操作是否已经结束。
- (BOOL)isCancelled;
判断操作是否已经标记为取消。
- (BOOL)isExecuting;
判断操作是否正在在运行。
- (BOOL)isReady;
判断操作是否处于准备就绪状态,这个值和操作的依赖关系相关。
3.操作同步
- (void)waitUntilFinished;
阻塞当前线程,直到该操作结束。可用于线程执行顺序的同步。
- (void)setCompletionBlock:(void (^)(void))block;
completionBlock 会在当前操作执行完毕时执行 completionBlock。
- (void)addDependency:(NSOperation *)op;
添加依赖,使当前操作依赖于操作 op 的完成。
- (void)removeDependency:(NSOperation *)op;
移除依赖,取消当前操作对操作 op 的依赖。
@property (readonly, copy) NSArray<NSOperation *> *dependencies;
在当前操作开始执行之前完成执行的所有操作对象数组。
NSOperationQueue 常用属性和方法
1.取消/暂停/恢复操作
- (void)cancelAllOperations;
可以取消队列的所有操作。
- (BOOL)isSuspended;
判断队列是否处于暂停状态。 YES 为暂停状态,NO 为恢复状态。
- (void)setSuspended:(BOOL)b;
可设置操作的暂停和恢复,YES 代表暂停队列,NO 代表恢复队列。
2.操作同步
- (void)waitUntilAllOperationsAreFinished;
阻塞当前线程,直到队列中的操作全部执行完毕。
3.添加/获取操作
- (void)addOperationWithBlock:(void (^)(void))block;
向队列中添加一个 NSBlockOperation 类型操作对象。
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait;
向队列中添加操作数组,wait 标志是否阻塞当前线程直到所有操作结束
- (NSArray *)operations;
当前在队列中的操作数组(某个操作执行结束后会自动从这个数组清除)。
- (NSUInteger)operationCount;
当前队列中的操作数。
4.获取队列
+ (id)currentQueue;
获取当前队列,如果当前线程不是在 NSOperationQueue 上运行则返回 nil。
+ (id)mainQueue;
获取主队列。
部分摘取自西单_夜未央
网友评论