zoukankan      html  css  js  c++  java
  • 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)

  • 相关阅读:
    node
    github
    [模块] pdf转图片-pdf2image
    python 15 自定义模块 随机数 时间模块
    python 14 装饰器
    python 13 内置函数II 匿名函数 闭包
    python 12 生成器 列表推导式 内置函数I
    python 11 函数名 迭代器
    python 10 形参角度 名称空间 加载顺序
    python 09 函数参数初识
  • 原文地址:https://www.cnblogs.com/muzijie/p/6438160.html
Copyright © 2011-2022 走看看