• NSOperation NSOperationQueue


    一. 简介

      除了使用NSThread和GCD可以实现多线程,配合使用NSOperation和NSOperationQueue也能实现多线程。

      使用NSOperation和NSOperationQueue实现多线程的操作步骤:

      1. 将需要执行的操作封装到NSOperation的子类对象中。

        实际上,NSOperaion是一个抽象类,并不可以封装操作,必须使用它的子类

      2. 将NSOperation对象添加到NSOperationQueue中

      3. 系统会自动将NSOperationQueue中的NSOperation取出

      4. 将取出的NSOperation封装的操作放到一个新线程中执行。

    二. NSOperation的使用

      1. 实现NSOperation的封装有三种:

        (1)NSInvocationOperation

           创建NSInvocationOperation对象,调用start方法开始执行操作。

           默认情况下,调用start方法后并不会开启一个新线程去执行操作,而是在当前线程同步执行操作。

             只有将NSOperation操作放到NSOperationQueue中,才会异步执行操作。

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(startOperation1:) object:nil];    
        [operation1 start];
        
        NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(startOperation2:) object:nil];
        [operation2 start];
    }
    
    - (void)startOperation1: (id)sender {
        NSLog(@"operation1---------%@", [NSThread currentThread]);
    }
    
    - (void)startOperation2:(id)sender {
        NSLog(@"operation2---------%@", [NSThread currentThread]);
    }
    
    // 打印结果:
    2017-02-24 12:58:11.696 OperationDemo[74424:8270352] operation1---------<NSThread: 0x7a86af70>{number = 1, name = main}
    2017-02-24 12:58:11.698 OperationDemo[74424:8270352] operation2---------<NSThread: 0x7a86af70>{number = 1, name = main}   
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];    
        
        NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(startOperation1:) object:nil];    
        [queue addOperation:operation1];
        
        NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(startOperation2:) object:nil];
        [queue addOperation:operation2];
    }
    
    - (void)startOperation1: (id)sender {
        NSLog(@"operation1---------%@", [NSThread currentThread]);
    }
    
    - (void)startOperation2:(id)sender {
        NSLog(@"operation2---------%@", [NSThread currentThread]);
    }
    
    // 打印结果:
    2017-02-24 13:13:19.351 OperationDemo[74639:8308964] operation2---------<NSThread: 0x7b6a2140>{number = 3, name = (null)}
    2017-02-24 13:13:19.351 OperationDemo[74639:8308962] operation1---------<NSThread: 0x7b8e5ee0>{number = 2, name = (null)}

        (2)NSBlockOperation

          创建NSBlockOperation对象,使用addExecutionBlock:方法添加更多的操作。

          只要NSBlockOperation封装的操作数大于1,就会异步执行操作。

     
        NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"operation1---------%@", [NSThread currentThread]);
        }];
        [operation start];
    
    // 打印结果:
    2017-02-24 13:20:39.094 OperationDemo[74785:8328635] operation1---------<NSThread: 0x78e315f0>{number = 1, name = main}
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"operation1---------%@", [NSThread currentThread]);
        }];
        
        [operation addExecutionBlock:^{
            NSLog(@"operation2---------%@", [NSThread currentThread]);
        }];
        
        [operation addExecutionBlock:^{
            NSLog(@"operation3---------%@", [NSThread currentThread]);
        }];
        
        [operation start];
    
    // 打印结果:
    2017-02-24 13:17:58.073 OperationDemo[74742:8321309] operation3---------<NSThread: 0x79072250>{number = 3, name = (null)}
    2017-02-24 13:17:58.073 OperationDemo[74742:8321307] operation2---------<NSThread: 0x78fb8840>{number = 2, name = (null)}
    2017-02-24 13:17:58.073 OperationDemo[74742:8321024] operation1---------<NSThread: 0x78f8e020>{number = 1, name = main}

        (3)自定义子类继承NSOperation,实现内部相应的main方法封装操作

          需要重写main方法,将操作任务放到main方法中。

    @implementation CustomOperation
    
    - (void)main {
    
        // 将操作任务放在这里
        NSLog(@"-----1-----");
    }
    
    @end

     三. NSOperationQueue

      1. NSOpeationQueue作用:

        NSOperation可以调用start方法执行任务,但默认是同步执行的。

        如果将NSOperation添加到NSOperationQueue中,系统会自动异步执行操作。

      2. 添加NSOperation到NSOperationQueue有两个方法:

        -(void)addOperation:(NSOperation *)operation;

        -(void)addOperationWithBlock:(void(^)(void))block;

        只要将一个操作添加到队列(默认是并行队列),那么队列内部会自动调用start方法。

        如果要将队列设置为串行,只需要设置队列的queue.maxConcurrentOperationCount = 1即可。

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];    
        
        NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(startOperation1:) object:nil];    
        [queue addOperation:operation1];
        
        NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(startOperation2:) object:nil];
        [queue addOperation:operation2];
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        [queue addOperationWithBlock:^{
            NSLog(@"operation1-------%@", [NSThread currentThread]);
        }];
        
        [queue addOperationWithBlock:^{
            NSLog(@"operation2-------%@", [NSThread currentThread]);
        }];
    
    // 打印结果:
    2017-02-24 13:30:52.198 OperationDemo[74890:8354590] operation2-------<NSThread: 0x7b6897d0>{number = 3, name = (null)}
    2017-02-24 13:30:52.198 OperationDemo[74890:8354591] operation1-------<NSThread: 0x7b750aa0>{number = 2, name = (null)}

    四. NSOperationQueue的串行和并发: 最大并发数

      队列的最大并发数为maxConcurrentOperationCount;

      默认情况下,maxConcurrentOperationCount = -1,代表不限制最大并发数,可以创建N多个线程;

      通过[[NSOperationQueue alloc] init] 创建的NSOperationQueue是并发的,如果想设置为串行,只需要将maxConcurrentOperationCount = 1;

      不可以将maxConcurrentOperationCount设置为0,否则任务将不会执行。

        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        queue.maxConcurrentOperationCount = 1;
        
        [queue addOperationWithBlock:^{
            NSLog(@"operation1-------%@", [NSThread currentThread]);
        }];
        
        [queue addOperationWithBlock:^{
            NSLog(@"operation2-------%@", [NSThread currentThread]);
        }];
    
    //打印结果:
    2017-02-24 13:36:23.018 OperationDemo[74951:8368501] operation1-------<NSThread: 0xa22dc70>{number = 2, name = (null)}
    2017-02-24 13:36:23.019 OperationDemo[74951:8368501] operation2-------<NSThread: 0xa22dc70>{number = 2, name = (null)}

     五. NSOperationQueue的取消、暂停、恢复

      可以通过NSOperation的cancel方法来取消单个操作。

      操作只要取消,就不会再恢复。

      取消任务和暂停任务一样,不会取消当前正在执行的操作,职能取消还未执行的操作。

      可以通过NSOperationQueue的cancelAllOperations方法来取消所有的操作。

      暂停和恢复任务:

        -(void)setSuspended:(Bool)suspend;   YES表示暂停,NO表示恢复。

        -(Bool)isSuspended;  判断队列是否在暂停中

      若要取消的任务是自定义的操作队列的话,执行完一个耗时操作后,需要增加是否取消任务的判断,再去执行另外一个操作任务。

        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        queue.maxConcurrentOperationCount = 1;
        
        NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"operation1------%@", [NSThread currentThread]);
        }];
        
        NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"operation2------%@", [NSThread currentThread]);
        }];
        
        NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"operation3------%@", [NSThread currentThread]);
        }];
        
        [queue addOperation:operation1];
        [queue addOperation:operation2];
        [queue addOperation:operation3];
        
        [operation3 cancel];
    
    // 打印结果:
    2017-02-24 13:43:43.052 OperationDemo[75069:8388603] operation1------<NSThread: 0x79466810>{number = 2, name = (null)}
    2017-02-24 13:43:43.054 OperationDemo[75069:8388603] operation2------<NSThread: 0x79466810>{number = 2, name = (null)}
    @implementation CustomOperation
    
    - (void)main {
    
        for (int i = 0; i < 10000; i ++) {
            NSLog(@"-----1-----%@-----", [NSThread currentThread]);
        }
        
        // 增加队列是否取消任务的判断
        if (self.isCancelled) {
            return;
        }
        
        for (int i = 0; i < 10000; i ++) {
            NSLog(@"-----2-----%@-----", [NSThread currentThread]);
        }
        
        // 增加队列是否取消任务的判断
        if (self.isCancelled) {
            return;
        }
        
        for (int i = 0; i < 10000; i ++) {
            NSLog(@"-----3-----%@-----", [NSThread currentThread]);
        }
        
        // 增加队列是否取消任务的判断
        if (self.isCancelled) {
            return;
        }
        
        for (int i = 0; i < 10000; i ++) {
            NSLog(@"-----4-----%@-----", [NSThread currentThread]);
        }
    }
    
    @end

    六. NSOperationQueue线程间通信

      开启子线程下载图片,下载完成后,在主线程更新UI。

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        [queue addOperationWithBlock:^{
            NSString *urlString = @"http://www.wallcoo.com/animal/Dogs_Summer_and_Winter/wallpapers/1920x1200/DogsB10_Lucy.jpg";
            NSURL *url = [NSURL URLWithString:urlString];
            NSData *data = [NSData dataWithContentsOfURL:url];
            if (data) {
                UIImage *image = [UIImage imageWithData:data];
                
                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                   
                    NSLog(@"更新UI");
                    _backImageView.image = image;
                    
                }];
            }
        }];  
    }

     七. 操作依赖

      可以在同一个queue中添加操作依赖,也可以在不痛的队列中添加操作依赖。

      注意:千万不要A依赖B,同时B也依赖A。

      经典实例:合成图片

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        
        // 队列1用于下载图片
        NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
        // 队列2用于合成图片
        NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
        
        __block UIImage *image1 = nil;
        __block UIImage *image2 = nil;
        
        // 下载图片1
        NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
            
            NSString *urlString = @"http://www.wallcoo.com/animal/Dogs_Summer_and_Winter/wallpapers/1920x1200/DogsB10_Lucy.jpg";
            NSURL *url = [NSURL URLWithString:urlString];
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *image = [UIImage imageWithData:data];
            image1 = image;
        }];
        
        // 下载图片2
        NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
            
            NSString *urlString = @"http://img4.duitang.com/uploads/item/201507/30/20150730163204_A24MX.thumb.700_0.jpeg";
            NSURL *url = [NSURL URLWithString:urlString];
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *image = [UIImage imageWithData:data];
            image2 = image;
        }];
        
        [operation1 setCompletionBlock:^{
            NSLog(@"图片1下载完成");
        }];
        
        [operation2 setCompletionBlock:^{
            NSLog(@"图片2下载完成");
        }];
        
        // 合成图片
        NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
            
            UIGraphicsBeginImageContext(CGSizeMake(200, 200));
            [image1 drawInRect:CGRectMake(0, 0, 100, 200)];
            [image2 drawInRect:CGRectMake(100, 0, 100, 200)];
            UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            
            // 主线程更新UI
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                NSLog(@"主线程更新UI");
                _backImageView.image = newImage;
            }];
            
        }];
        
        // 合成图片依赖于下载图片---只有图片下载完成,才可以进行合成操作
        [operation3 addDependency:operation1];
        [operation3 addDependency:operation2];    
        
        // 将下载图片添加到队列1中
        [queue1 addOperation:operation1];
        [queue1 addOperation:operation2];
        
        // 将合成图片添加到队列2中
        [queue2 addOperation:operation3];    
    }

     摘自:iOS NSOperation(http://www.jianshu.com/p/adb075114246)

  • 相关阅读:
    关于大型网站技术演进的思考(二)--存储的瓶颈(2)[转]
    根据 Sourcemap 调试打包后的js
    webpack 中某些配置
    Javascript 中的数组
    浮动元素的display属性
    安装升级npm依赖
    锚点定位
    我所认识的java泛型(主要讨论通配符的使用)
    快速排序的递归非递归实习java
    java 选择排序
  • 原文地址:https://www.cnblogs.com/muzijie/p/6438160.html
Copyright © 2020-2023  润新知