NSOperation的核心概念和GCD非常相似,NSOperation是将“操作”添加到“队列”中。NSOperation是一个抽象类,不能直接使用,其目的就是为了定义子类共有的方法和属性。其子类有两个:NSInvocationOperation、NSBlockOperation。(废话不多说,直接上代码👇)
一、NSInvocationOperation
- (void)viewDidLoad {
[super viewDidLoad];
//1.队列
NSOperationQueue * q = [[NSOperationQueue alloc]init];
for (int i = 0; i < 10; i++) {
//初始化NSInvocationOperation,执行一个操作。
NSInvocationOperation * op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@(i)];
//添加到队列
[q addOperation:op];
}
}
- (void)downloadImage:(id)objc {
NSLog(@"%@ %@",[NSThread currentThread],objc);
}
打印结果:
这个结果告诉我们:开启多个线程,不会顺序执行 --> GCD 并发队列,异步执行
队列:本质上就是GCD的并发队列!!!
操作:就是异步执行任务!!!
二、NSBlockOperation
-(void) viewDidLoad{
[super viewDidLoad];
//1.队列
NSOperationQueue * q = [[NSOperationQueue alloc]init];
//2.操作
for (int i = 0; i < 10; i++) {
NSBlockOperation * op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@ --- %d",[NSThread currentThread],i);
}];
//添加到队列
[q addOperation:op];
}
}
结果就不多说了,肯定和NSInvocationOperation一样的。但是,你们有木有发现,NSBlockOperation使用起来更加方便简单呢,而且代码都在一起,便于维护!!!
就是这么简单,其实NSOperation到这里就差不多讲完了。。(别,别动手,不准打脸。我再写点东西出来。。。)
还有一个更加简单的方法哈,👀👇!!!
在开发过程中,我们肯定要使用全局队列的。先声明一个全局队列属性
@property(nonatomic,strong)NSOperationQueue * opQueue;
再懒加载一下
//懒加载
-(NSOperationQueue *)opQueue
{
if (!_opQueue) {
_opQueue = [[NSOperationQueue alloc]init];
}
return _opQueue;
}
重点来了
-(void) viewDidLoad{
[super viewDidLoad];
//直接添加任务
for (int i = 0; i < 10; i++) {
[self.opQueue addOperationWithBlock:^{
NSLog(@"%@ --- %d",[NSThread currentThread],i);
}];
}
}
在NSOperation里没有最简单,只有更简单。哈哈😀!
结果就不用看了吧,和上面一样哦!!!
提示:只要是NSOperation 的子类,都可以添加到队列!
三、线程间的通讯
-(void) viewDidLoad{
[super viewDidLoad];
[self.opQueue addOperationWithBlock:^{
NSLog(@"耗时操作 %@",[NSThread currentThread]);
//主线程更新 UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"UIJIUUIIUIU --- %@",[NSThread currentThread]);
}];
}];
}
上面这段代码就不讲解了,一眼就看懂了!!!
NSOperaton 是苹果大力推荐的"并发"技术!
小结:
GCD & NSOperation 对比
GCD 在 iOS 4.0 推出,主要针对多核处理器做了优化的并发技术,是C语言的
- 将"任务"[block]添加到 队列[串行/并发/主队列/全局队列] ,并且指定执行任务的函数[同步/异步]
- 线程间的通讯 dispatch_get_main_queue()
- 提供了一些 NSOperation 不具备的功能
- 一次执行
- 延迟执行
- 调度组(在op中也可以做到,有点麻烦)
NSOperation 在 iOS 2.0 推出的,苹果推出 GCD以后,对NSOperation 底层做了重写!
- 将操作[异步执行的任务] 添加到队列[并发队列],就会立刻异步执行
- 线程间的通讯mainQueue
- 提供了一些GCD 实现起来比较困难的功能
- 最大并发线程
- 队列的暂停/继续
- 取消所有操作
- 指定操作之间的依赖关系(GCD 用同步来实现)
四、最大并发数
/*
从 iOS 8.0 开始,无论使用 GCD还是 NSOperation ,都会开启很多线程
在 iOS 7.0 以前,GCD 通常只会开启 5 6条线程!
目前线程多了说明:
1.底层的现场池更大了,能够拿到的线程资源多了!
2.多控制同时并发的现场数,要求就更高了!
*/
-(void)viewDidLoad{
[super viewDidLoad];
//设置同时最大的并发操作数量
//WIFI: 5 至 6
//流量 : 2 到 3
self.opQueue.maxConcurrentOperationCount = 2;
//添加操作进队列
for (int i = 0;i < 20; i++) {
[self.opQueue addOperationWithBlock:^{
//当前线程睡一秒
[NSThread sleepForTimeInterval:1.0];
NSLog(@"%@---%d",[NSThread currentThread],i);
}];
}
}
打印结果:
先看时间:每隔一秒,打印两次。没毛病吧,最大并发数为2嘛!!!
再看线程数:(别丢鸡蛋啊,我还没讲完。。。)有人就说了不是最大并发数为2嘛,线程数5和6是哪里来的?看下面!!!
知识点补充:子线程在完成任务以后,线程池会对其进行回收。上个任务执行完毕后,也会有调度下个任务的操作。当回收线程和调度任务相遇,那么就会产生新线程了。线程在回收的过程中,就不能执行任务了。这时就少了一个线程,所以线程池会再分配一条线程出来执行任务。所以5和6就这样产生了,并不是我忽悠你们啊。真的最多就只有两条线程(除主线程外,切记!!!)
五、队列挂起&取消所有操作
1、暂停&继续
//判断我们队列是否挂起
if(self.opQueue.isSuspended){
NSLog(@"继续 %tu",self.opQueue.operationCount);
self.opQueue.suspended = NO;
}else{
NSLog(@"暂停%tu",self.opQueue.operationCount);
self.opQueue.suspended = YES;
}
当挂起队列的时候,正在执行的操作不受影响!
suspended : 决定队列的暂停和继续
operationCount : 队列中的操作数
2、取消所有操作
NSLog(@"取消所有操作");
//取消操作
[self.opQueue cancelAllOperations];
NSLog(@"取消之后的操作数 :%tu",self.opQueue.operationCount);
1.队列挂起的时候,不会清空内部的操作.只有在队列继续的时候才会清空!
2.正在执行的操作也不会被取消!
上面的代码就不贴打印结果验证了,结论我用文字写了。剩下的就留给你们自己去验证吧!!!(有错的话,请留言指出)
六、依赖关系
-(void)viewDidLoad{
[super viewDidLoad];
/*
例子: 下载\解压\通知用户
*/
//1.下载
NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载---%@",[NSThread currentThread]);
}];
//2.解压
NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"解压---%@",[NSThread currentThread]);
}];
//3.通知用户
NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"通知用户---%@",[NSThread currentThread]);
}];
//NSOperation 提供了依赖关系
//!!!! 注意,不要指定循环依赖,不然队列就不工作了!!
[op2 addDependency:op1];
[op3 addDependency:op2];
//添加到队列中 waitUntilFinished:是否等待! YES,会卡住当前线程!!(不要在主线程中写YES,除非业务逻辑需要)
[self.opQueue addOperations:@[op1,op2] waitUntilFinished:NO];
//主线程通知用户
[[NSOperationQueue mainQueue] addOperation:op3];
}
代码都给你们了,也写好了注释,因为NSOperation太简单易懂了,我想装逼多说一些都没办法,就这样吧。
多线程到这里就。。。。END!!!
有问题多多指出,我们一起进步!(点个关注呗!!!)