美文网首页iOS日常须知
iOS开发之NSOperation深入浅出

iOS开发之NSOperation深入浅出

作者: 赤小豆nil | 来源:发表于2017-12-03 00:56 被阅读22次

    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深入浅出

        本文链接:https://www.haomeiwen.com/subject/sfxubxtx.html