• iOS多线程_07_NSOperation


    一、NSOperation的基本概念

      NSOperation是OC的,GCD是C语言的

    1、NSOperation的作用

      配合使用NSOperation和NSOperationQueue也能实现多线程编程,跟GCD的队列、任务很像。

    2、NSOperation和NSOperationQueue实现多线程的具体步骤

    • 先将需要执行的操作封装到一个NSOperation对象中
    • 然后将NSOperation对象添加到NSOperationQueue中
    • 系统会自动将NSOperationQueue中的NSOperation取出来
    • 将取出的NSOperation封装的操作放到一条新线程中执行

      与GCD相比,NSOperation没有串行、并行,同步、异步,的概念。

    3、NSOperation的子类

    (1)NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类

    (2)使用NSOperation子类的方式有3种

        NSInvocationOperation

        NSBlockOperation

        自定义子类继承NSOperation,实现内部相应的方法

    二、NSInvocationOperation

      invocation :调度

    1、创建NSInvocationOperation对象

    - (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg; 

    2、调用start方法开始执行操作

    - (void)start;

      一旦执行操作,就会调用target的sel方法

    3、注意

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

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

    4、默认情况

     1 @interface MAViewController ()
     2 /** NSOperation操作队列 */
     3 @property (strong, nonatomic) NSOperationQueue *queue;
     4 @end
     5 
     6 @implementation MAViewController
     7 
     8 // 将操作添加到队列即可
     9 - (NSOperationQueue *)queue
    10 {
    11     if (!_queue) _queue = [[NSOperationQueue alloc] init];
    12     return _queue;
    13 }
    14 
    15 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    16 {
    17     [self opDemo1];
    18 }
    19 
    20 #pragma mark - NSOperation
    21 
    22 - (void)download:(id)obj
    23 {
    24     NSLog(@"下载开始 %@ -- %@", [NSThread currentThread], obj);
    25 }
    26 
    27 - (void)opDemo1
    28 {
    29     NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download:) object:@"op1"];
    30   // 如果直接启动,会在主线程执行
    31 [op1 start];
    32 }

      输出结果:

        下载开始 <NSThread: 0x8d65a90>{name = (null), num = 1} -- op1

    5、将NSOperation(即NSInvocationOperation)放到NSOperationQueue中

      修改opDemo1中的代码如下:

    - (void)opDemo1
    {
        NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download:) object:@"op1"];
    
        // 添加到队列,就会新建线程,异步执行
        [self.queue addOperation:op1];
    }

      输出结果:

        下载开始 <NSThread: 0x8c720d0>{name = (null), num = 2} -- op1

      结果说明,NSInvocationOperation操作放到了NSOperationQueue队列中,新建线程,异步执行。

    6、多来几次添加到NSOperationQueue队列中

    - (void)opDemo1
    {
        for (int i = 0; i < 10; i++) {
            NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download:) object:@(i)];
            
            // 添加到队列,就会新建线程,异步执行
            [self.queue addOperation:op1];
        }
    }

      输出结果:

        下载开始 <NSThread: 0x8d5ca80>{name = (null), num = 3} -- 1

        下载开始 <NSThread: 0x8c800b0>{name = (null), num = 4} -- 2

        下载开始 <NSThread: 0x8c7f8f0>{name = (null), num = 2} -- 0

        下载开始 <NSThread: 0x8c80850>{name = (null), num = 5} -- 3

        下载开始 <NSThread: 0x8d5ca80>{name = (null), num = 3} -- 6

        下载开始 <NSThread: 0x8c800b0>{name = (null), num = 4} -- 4

        下载开始 <NSThread: 0x8c80850>{name = (null), num = 5} -- 7

        下载开始 <NSThread: 0x8c7f8f0>{name = (null), num = 2} -- 5

        下载开始 <NSThread: 0x8c800b0>{name = (null), num = 4} -- 9

        下载开始 <NSThread: 0x8d5ca80>{name = (null), num = 3} -- 8

      说明NSInvocationOperation操作放到了NSOperationQueue队列中,是并行、异步的。

    三、NSBlockOperation

    1、创建 NSBlockOperation 对象

    + (id)blockOperationWithBlock:(void (^)(void))block;

    2、把 NSBlockOperation 放到队列中

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        [self opDemo2];
    }
    
    - (void)opDemo2
    {
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"下载开始 %@ -- %@", [NSThread currentThread], nil);
        }];
        
        [self.queue addOperation:op1];
    }

      输出结果:下载开始 <NSThread: 0xd14ae30>{name = (null), num = 2} -- (null)

      多来几次,将 NSBlockOperation 对象添加到队列中

    - (void)opDemo2
    {
        for (int i = 0; i < 10; i++) {
            NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
                NSLog(@"下载开始 %@ -- %d", [NSThread currentThread], i);
            }];
            
            [self.queue addOperation:op1];
        }
    }

      输出结果:

        下载开始 <NSThread: 0x8d78b80>{name = (null), num = 2} -- 2

        下载开始 <NSThread: 0x9b5fbe0>{name = (null), num = 5} -- 3

        下载开始 <NSThread: 0x9b68e50>{name = (null), num = 3} -- 1

        下载开始 <NSThread: 0x8cb1280>{name = (null), num = 4} -- 0

        下载开始 <NSThread: 0x9b5fbe0>{name = (null), num = 5} -- 7

        下载开始 <NSThread: 0x8d78b80>{name = (null), num = 2} -- 5

        下载开始 <NSThread: 0x9b68e50>{name = (null), num = 3} -- 6

        下载开始 <NSThread: 0x8cb1280>{name = (null), num = 4} -- 4

        下载开始 <NSThread: 0x9b5fbe0>{name = (null), num = 5} -- 8

        下载开始 <NSThread: 0x8d78b80>{name = (null), num = 2} -- 9

      两次结果说明,将NSBlockOperation对象添加到NSOperationQueue中,是并行,异步的

      而且,可以发现,NSBlockOperation比NSInvocationOperation操作更简单,少了一步调用download的操作,看上去更简洁

    3、更简单的形式

    - (void)opDemo3
    {
        for (int i = 0; i < 10; i++) {
            [self.queue addOperationWithBlock:^{
                NSLog(@"下载开始 %@ -- %d", [NSThread currentThread], i);
            }];
        }
    }

      输出结果:

        下载开始 <NSThread: 0x8d78b80>{name = (null), num = 2} -- 2

        下载开始 <NSThread: 0x9b5fbe0>{name = (null), num = 5} -- 3

        下载开始 <NSThread: 0x9b68e50>{name = (null), num = 3} -- 1

        下载开始 <NSThread: 0x8cb1280>{name = (null), num = 4} -- 0

        下载开始 <NSThread: 0x9b5fbe0>{name = (null), num = 5} -- 7

        下载开始 <NSThread: 0x8d78b80>{name = (null), num = 2} -- 5

        下载开始 <NSThread: 0x9b68e50>{name = (null), num = 3} -- 6

        下载开始 <NSThread: 0x8cb1280>{name = (null), num = 4} -- 4

        下载开始 <NSThread: 0x9b5fbe0>{name = (null), num = 5} -- 8

        下载开始 <NSThread: 0x8d78b80>{name = (null), num = 2} -- 9

       结果还是并行,异步的。而且形式更加简单

    4、向主队列中添加操作

    - (void)opDemo3
    {
        for (int i = 0; i < 10; i++) {
            // 只要将操作添加到队列,就会立即被调度(执行)
            [self.queue addOperationWithBlock:^{
                NSLog(@"下载开始 %@ -- %d", [NSThread currentThread], i);
            }];
        }
        
        // 向主队列中添加操作
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"下载开始 %@ -- %@", [NSThread currentThread], nil);
        }];
    }

      输出结果:

        下载开始 <NSThread: 0x8c7f480>{name = (null), num = 5} -- 1

        下载开始 <NSThread: 0x8c76710>{name = (null), num = 1} -- (null)

        下载开始 <NSThread: 0x8e70eb0>{name = (null), num = 3} -- 2

        下载开始 <NSThread: 0x8e71e20>{name = (null), num = 4} -- 3

        下载开始 <NSThread: 0x8c7b030>{name = (null), num = 2} -- 0

        下载开始 <NSThread: 0x8c7f480>{name = (null), num = 5} -- 4

        下载开始 <NSThread: 0x8e70eb0>{name = (null), num = 3} -- 5

        下载开始 <NSThread: 0x8e711e0>{name = (null), num = 6} -- 6

        下载开始 <NSThread: 0x8e71e20>{name = (null), num = 4} -- 7

        下载开始 <NSThread: 0x8e70eb0>{name = (null), num = 3} -- 9

        下载开始 <NSThread: 0x8c7f480>{name = (null), num = 5} -- 8

       可以看到向主队列添加的操作与子线程中的操作是同时执行的,而不是子线程执行完才执行主线程

    5、通过addExecutionBlock:方法添加更多的操作

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

      注意:只要NSBlockOperation封装的操作数 > 1,就会异步执行操作。

    (1)通过代码来研究 addExecutionBlock 的作用:

        // 实例化block操作
        NSBlockOperation *op = [[NSBlockOperation alloc] init];
        
        // 添加执行块
        [op addExecutionBlock:^{
            NSLog(@"下载书籍 %@", [NSThread currentThread]);
        }];
        
        [op start];

      输出结果:下载书籍 <NSThread: 0x8f22920>{name = (null), num = 1}

      可以看到 NSBlockOperation封装的操作数 = 1 就会在主线程执行

    (2)如果大于1,呢?

        // 实例化block操作
        NSBlockOperation *op = [[NSBlockOperation alloc] init];
        
        // 添加执行块
        [op addExecutionBlock:^{
            NSLog(@"下载书籍1 %@", [NSThread currentThread]);
        }];
        
        // 继续添加块
        [op addExecutionBlock:^{
            NSLog(@"下载书籍2 %@", [NSThread currentThread]);
        }];
        
        // 继续添加块
        [op addExecutionBlock:^{
            NSLog(@"下载书籍3 %@", [NSThread currentThread]);
        }];
        
        // 继续添加块
        [op addExecutionBlock:^{
            NSLog(@"下载书籍4 %@", [NSThread currentThread]);
        }];
        
        // 继续添加块
        [op addExecutionBlock:^{
            NSLog(@"下载书籍5 %@", [NSThread currentThread]);
        }];
        
        [op start];

      输出结果:

      多线程调试,永远不要相信一次的运行结果,再次运行结果:

      这两个结果说明,只要NSBlockOperation封装的操作数 > 1,就会异步执行操作。而且执行块不一定哪个会被放到主线程。

    (3)如果添加到 NSOperationQueue 队列中

        //    [op start];
        [self.queue addOperation:op];

      输出结果:

      结果说明,放到队列中就全部异步执行了,直接启动是有在主线程,有在子线程的。具体开启线程的数量,由系统决定。

      执行块的调度与操作的调度非常像。

  • 相关阅读:
    Windows Server 2008 R2系统上安装SQLServer2012集群(简略)
    播放视频有沙沙的杂音怎么处理?
    合并两个表取数
    电脑下边的语言栏跑到左边了怎么办?
    一条SQL查出当月的每一天
    统一本地服务器名和计算机名
    一个奇葩的SQL
    Cent Os7.0安装MongoDB4.2.0
    MySQL 8.0.5开启远程连接
    MySQL系统函数及SQL开发中易犯错的点
  • 原文地址:https://www.cnblogs.com/yudigege/p/3932758.html
Copyright © 2020-2023  润新知