iOS开发之NSOperation深入浅出

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);
}

打印结果:


image.png

这个结果告诉我们:开启多个线程,不会顺序执行 --> 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);
        }];
    }
}

打印结果:


image.png

先看时间:每隔一秒,打印两次。没毛病吧,最大并发数为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!!!

有问题多多指出,我们一起进步!(点个关注呗!!!)

你可能感兴趣的:(iOS开发之NSOperation深入浅出)