[New learn] NSOperation基本使用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[New learn] NSOperation基本使用相关的知识,希望对你有一定的参考价值。

1.简介

NS(基于OC语言)是对GCD(基于C语言)的封装,让开发者能够更加友好的方便的去使用多线程技术。

 

2.NSOperation的基本使用

NSOperation是抽象类,所以如果要使用NSOperation则需要继承和实现它。

内置的子类常用的有两个:

  • NSInvocationOperation
  • NSBlockOperation

使用步骤:

  无论上述哪一个一般使用都遵循以下步骤:

  1. 创建队列/获取主队列
  2. 创建子类实例
  3. 加入队列

      例子:https://github.com/xufeng79x/NSOperation

 

3.NSOperation再研究

NSOperation 中有这么一句话:

An operation object is a single-shot object—that is, it executes its task once and cannot be used to execute it again.

所以说一个操作只能被执行一次,当如下代码将某个操作加到多个队列的时候会出现错误 

    [queue1 addOperation:op1];

    [queue2 addOperation:op1];

 错误:

2016-03-01 16:30:40.425 NSOperation-Test[2430:148961] *** Terminating app due to uncaught exception NSInvalidArgumentException, reason: *** -[NSOperationQueue addOperation:]: operation is already enqueued on a queue

 

4.线程间通信

和GCD一样,当在某个线程中获取到信息需要更新UI的时候,那么更新UI需要在主线程中进行,所以这个就涉及到线程间的通信。 

// MARK: 线程间通信

- (void) test_threads_com
{
    //1. 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    //2. 在其他形成做耗时操作
    [queue addOperationWithBlock:^{
        NSLog(@"耗时操作---------%@", [NSThread currentThread]);
        
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"更新UI---------%@", [NSThread currentThread]);
        }];
    }];
}

上述代码通过队列的不同从而让人物去在不同线程中执行,main队列中的人物都是在主线程中执行的。

 

5.高级操作

介绍最大并发数,挂起,取消全部线程

注意:以上都是在队列上操作的,只对队列中未运行的线程其作用,而对正在运行和已经运行完毕的线程无效。

最大并发数:

// MARK:最大并发数
- (void)test_MaxThreadsNum
{
    //1. 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    //2. 设定队列上的最大并发数
    [queue setMaxConcurrentOperationCount:2];
    
    for (int i = 0; i < 20; i++) {
        //3. 直接将operation放到queue中
        [queue addOperationWithBlock:^{
            [NSThread sleepForTimeInterval:5.0f];
            NSLog(@"------------%@-------%d", [NSThread currentThread], i);
        }];
    }
}

 

输出:

技术分享

 

可以看到每一次只有两个线程运行。

 

挂起:

 

// MARK:挂起
// 测试步骤:新增全局队列,运行的时候点击暂停/开始按钮来让队列挂起或者取消挂起
- (void)test_suspend
{
    // 设定最大并发数,便于观察
    [self.globQueue setMaxConcurrentOperationCount:2];
    for (int i = 0; i < 20; i++) {
        //1直接将operation放到queue中
        [self.globQueue addOperationWithBlock:^{
            [NSThread sleepForTimeInterval:5.0f];
            NSLog(@"------------%@-------%d", [NSThread currentThread], i);
        }];
    }
}

 

 

 

测试结果:

技术分享

当程序运行的时候点击【暂定】后程序不再运行队列中的操作,当我们再次点击【开始】后继续运行队列中剩余的操作。

如前所述,挂起操作也是对于队列中的操作,而对已经运行的操作则不影响,所以我们看到暂停日志后又有两个任务打印出了结束日志。

另外一个细节就是当点击暂定时候队列中显示还有16个,再次点击开始的时候发现队列中还有14个,这说明只有当操作运行完毕后才会从队列中移除。

 

取消全部:

// MARK:取消全部
// 测试步骤:点击取消全部按钮
- (void)test_cancelALl
{
    // 设定最大并发数,便于观察
    [self.globQueue setMaxConcurrentOperationCount:2];
    for (int i = 0; i < 20; i++) {
        //1直接将operation放到queue中
        [self.globQueue addOperationWithBlock:^{
            [NSThread sleepForTimeInterval:5.0f];
            NSLog(@"------------%@-------%d", [NSThread currentThread], i);
        }];
    }
}

 

测试结果:

2016-03-04 11:20:06.469 NSOperation-Test[1379:43730] ------------<NSThread: 0x7b297590>{number = 6, name = (null)}-------0
2016-03-04 11:20:06.469 NSOperation-Test[1379:43912] ------------<NSThread: 0x7b2803d0>{number = 7, name = (null)}-------1
2016-03-04 11:20:10.084 NSOperation-Test[1379:43511] the queue has been cancelall! 18 in queue now!
2016-03-04 11:20:11.471 NSOperation-Test[1379:43912] ------------<NSThread: 0x7b2803d0>{number = 7, name = (null)}-------3
2016-03-04 11:20:11.471 NSOperation-Test[1379:43730] ------------<NSThread: 0x7b297590>{number = 6, name = (null)}-------2
2016-03-04 11:20:12.351 NSOperation-Test[1379:43511] the queue has been cancelall! 0 in queue now!

 

可以看到当运行的时候点击取消后后续所有任务都不会再运行。

值得注意的是点击取消后,还有运行日志打出,还是和之前所述一样,取消全部不会影响正在运行的操作。

另外需要注意的是,当点击取消的时候,日志还显示还有18个任务,当正在运行的操作完毕后,再次点击取消则显示为0个。

说明只有运行的任务运行完毕后才会清空队列。

 

思考--队列中的任务会按序执行吗?

// MARK:是否按序执行
- (void)test_executeOrder
{
    // 设定最大并发数为1,便于观察
    [self.globQueue setMaxConcurrentOperationCount:1];
    for (int i = 0; i < 20; i++) {
        //1直接将operation放到queue中
        [self.globQueue addOperationWithBlock:^{
            //[NSThread sleepForTimeInterval:1.0f];
            NSLog(@"------------%@-------%d", [NSThread currentThread], i);
        }];
    }
}

 

日志:

2016-03-04 11:32:25.530 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------0
2016-03-04 11:32:25.531 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------1
2016-03-04 11:32:25.531 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------2
2016-03-04 11:32:25.531 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------3
2016-03-04 11:32:25.531 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------4
2016-03-04 11:32:25.532 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------5
2016-03-04 11:32:25.532 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------6
2016-03-04 11:32:25.532 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------7
2016-03-04 11:32:25.532 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------8
2016-03-04 11:32:25.533 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------9
2016-03-04 11:32:25.533 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------10
2016-03-04 11:32:25.533 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------11
2016-03-04 11:32:25.533 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------12
2016-03-04 11:32:25.534 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------13
2016-03-04 11:32:25.534 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------14
2016-03-04 11:32:25.534 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------15
2016-03-04 11:32:25.535 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------16
2016-03-04 11:32:25.535 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------17
2016-03-04 11:32:25.535 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------18
2016-03-04 11:32:25.535 NSOperation-Test[1465:48115] ------------<NSThread: 0x7a644ae0>{number = 3, name = (null)}-------19

 

经过多次测试,发现是先进先出的有序执行。

 

 6.操作依赖

-线性依赖:

在NSOperation中可以进行操作的依赖设置,一个操作依赖于另外一个操作,可以在不同队列间的操作间设置依赖。

// MARK:操作间依赖
- (void)test_dependsLine
{
    //1. 建立第一个队列
    NSOperationQueue *queue1 = [[NSOperationQueue alloc]init];
    NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op1 in queue1------%@", [NSThread currentThread]);
    }];
    
    //2. 建立第二个队列
    NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
    NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op2 in queue2------%@", [NSThread currentThread]);
    }];
    
    //3. 建立第二个队列
    NSOperationQueue *queue3 = [[NSOperationQueue alloc] init];
    NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op3 in queue3------%@", [NSThread currentThread]);
    }];
    
    // 设定依赖关系
    [op3 addDependency:op2];
    [op2 addDependency:op1];
    
    // 一旦放入队列则将执行
    [queue1 addOperation:op1];
    [queue2 addOperation:op2];
    [queue3 addOperation:op3];
}

 

 

结果:

2016-03-05 00:26:01.803 NSOperation-Test[660:12848] ------I am op1 in queue1------<NSThread: 0x79f3b4b0>{number = 2, name = (null)}
2016-03-05 00:26:01.804 NSOperation-Test[660:12845] ------I am op2 in queue2------<NSThread: 0x79f39120>{number = 3, name = (null)}
2016-03-05 00:26:01.804 NSOperation-Test[660:12845] ------I am op3 in queue3------<NSThread: 0x79f39120>{number = 3, name = (null)}
2016-03-05 00:26:04.772 NSOperation-Test[660:12847] ------I am op1 in queue1------<NSThread: 0x7b217900>{number = 4, name = (null)}
2016-03-05 00:26:04.773 NSOperation-Test[660:12847] ------I am op2 in queue2------<NSThread: 0x7b217900>{number = 4, name = (null)}
2016-03-05 00:26:04.773 NSOperation-Test[660:12847] ------I am op3 in queue3------<NSThread: 0x7b217900>{number = 4, name = (null)}
2016-03-05 00:26:05.844 NSOperation-Test[660:12845] ------I am op1 in queue1------<NSThread: 0x79f39120>{number = 3, name = (null)}
2016-03-05 00:26:05.844 NSOperation-Test[660:12845] ------I am op2 in queue2------<NSThread: 0x79f39120>{number = 3, name = (null)}
2016-03-05 00:26:05.845 NSOperation-Test[660:12845] ------I am op3 in queue3------<NSThread: 0x79f39120>{number = 3, name = (null)}
2016-03-05 00:26:06.657 NSOperation-Test[660:12848] ------I am op1 in queue1------<NSThread: 0x79f3b4b0>{number = 2, name = (null)}
2016-03-05 00:26:06.658 NSOperation-Test[660:12848] ------I am op2 in queue2------<NSThread: 0x79f3b4b0>{number = 2, name = (null)}
2016-03-05 00:26:06.658 NSOperation-Test[660:12848] ------I am op3 in queue3------<NSThread: 0x79f3b4b0>{number = 2, name = (null)}
2016-03-05 00:26:07.608 NSOperation-Test[660:12848] ------I am op1 in queue1------<NSThread: 0x79f3b4b0>{number = 2, name = (null)}
2016-03-05 00:26:07.608 NSOperation-Test[660:12848] ------I am op2 in queue2------<NSThread: 0x79f3b4b0>{number = 2, name = (null)}
2016-03-05 00:26:07.609 NSOperation-Test[660:12848] ------I am op3 in queue3------<NSThread: 0x79f3b4b0>{number = 2, name = (null)}
2016-03-05 00:26:08.200 NSOperation-Test[660:12848] ------I am op1 in queue1------<NSThread: 0x79f3b4b0>{number = 2, name = (null)}
2016-03-05 00:26:08.202 NSOperation-Test[660:12848] ------I am op2 in queue2------<NSThread: 0x79f3b4b0>{number = 2, name = (null)}
2016-03-05 00:26:08.202 NSOperation-Test[660:12848] ------I am op3 in queue3------<NSThread: 0x79f3b4b0>{number = 2, name = (null)}
2016-03-05 00:26:08.566 NSOperation-Test[660:12848] ------I am op1 in queue1------<NSThread: 0x79f3b4b0>{number = 2, name = (null)}
2016-03-05 00:26:08.568 NSOperation-Test[660:12845] ------I am op2 in queue2------<NSThread: 0x79f39120>{number = 3, name = (null)}
2016-03-05 00:26:08.568 NSOperation-Test[660:12845] ------I am op3 in queue3------<NSThread: 0x79f39120>{number = 3, name = (null)}
2016-03-05 00:26:08.875 NSOperation-Test[660:12848] ------I am op1 in queue1------<NSThread: 0x79f3b4b0>{number = 2, name = (null)}
2016-03-05 00:26:08.876 NSOperation-Test[660:12848] ------I am op2 in queue2------<NSThread: 0x79f3b4b0>{number = 2, name = (null)}
2016-03-05 00:26:08.877 NSOperation-Test[660:12848] ------I am op3 in queue3------<NSThread: 0x79f3b4b0>{number = 2, name = (null)}

 

可以看到即使在不同的队列中操作依然按照设定的顺序执行。

-树形依赖

// MARK:操作间依赖-树形依赖
- (void)test_dependstree
{
    //1. 建立第一个队列
    NSOperationQueue *queue1 = [[NSOperationQueue alloc]init];
    NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op1 in queue1------%@", [NSThread currentThread]);
    }];
    
    //2. 建立第二个队列
    NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
    NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op2 in queue2------%@", [NSThread currentThread]);
    }];
    
    //3. 建立第二个队列
    NSOperationQueue *queue3 = [[NSOperationQueue alloc] init];
    NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op3 in queue3------%@", [NSThread currentThread]);
    }];
    
    // 设定依赖关系
    [op3 addDependency:op2];
    [op3 addDependency:op1];
    
    // 一旦放入队列则将执行
    [queue1 addOperation:op1];
    [queue2 addOperation:op2];
    [queue3 addOperation:op3];
}

 

结果:

2016-03-05 00:41:47.679 NSOperation-Test[712:16784] ------I am op2 in queue2------<NSThread: 0x78e2d160>{number = 3, name = (null)}
2016-03-05 00:41:47.679 NSOperation-Test[712:16785] ------I am op1 in queue1------<NSThread: 0x78e2bd70>{number = 2, name = (null)}
2016-03-05 00:41:47.680 NSOperation-Test[712:16784] ------I am op3 in queue3------<NSThread: 0x78e2d160>{number = 3, name = (null)}
2016-03-05 00:41:49.702 NSOperation-Test[712:16787] ------I am op1 in queue1------<NSThread: 0x79999660>{number = 4, name = (null)}
2016-03-05 00:41:49.703 NSOperation-Test[712:16785] ------I am op2 in queue2------<NSThread: 0x78e2bd70>{number = 2, name = (null)}
2016-03-05 00:41:49.703 NSOperation-Test[712:16785] ------I am op3 in queue3------<NSThread: 0x78e2bd70>{number = 2, name = (null)}
2016-03-05 00:41:50.165 NSOperation-Test[712:16787] ------I am op1 in queue1------<NSThread: 0x79999660>{number = 4, name = (null)}
2016-03-05 00:41:50.165 NSOperation-Test[712:16785] ------I am op2 in queue2------<NSThread: 0x78e2bd70>{number = 2, name = (null)}
2016-03-05 00:41:50.166 NSOperation-Test[712:16785] ------I am op3 in queue3------<NSThread: 0x78e2bd70>{number = 2, name = (null)}

 

可以看到op3需要等待op1和op2执行完毕后才会去执行。

 

-思考:可以先放队列在设定依赖吗?

// MARK:操作间依赖-先放队列后设定依赖
- (void)test_dependAfterQueued
{
    //1. 建立第一个队列
    NSOperationQueue *queue1 = [[NSOperationQueue alloc]init];
    NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op1 in queue1------%@", [NSThread currentThread]);
    }];
    
    //2. 建立第二个队列
    NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
    NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op2 in queue2------%@", [NSThread currentThread]);
    }];
    
    //3. 建立第二个队列
    NSOperationQueue *queue3 = [[NSOperationQueue alloc] init];
    NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"------I am op3 in queue3------%@", [NSThread currentThread]);
    }];
    

    
    // 一旦放入队列则将执行
    [queue1 addOperation:op1];
    [queue2 addOperation:op2];
    [queue3 addOperation:op3];
    
    // 设定依赖关系
    [op3 addDependency:op2];
    [op3 addDependency:op1];
}

 

结果:

2016-03-05 00:46:40.158 NSOperation-Test[747:18522] ------I am op2 in queue2------<NSThread: 0x7a66f630>{number = 2, name = (null)}
2016-03-05 00:46:40.158 NSOperation-Test[747:18521] ------I am op1 in queue1------<NSThread: 0x7b122120>{number = 4, name = (null)}
2016-03-05 00:46:40.158 NSOperation-Test[747:18523] ------I am op3 in queue3------<NSThread: 0x7b227d60>{number = 3, name = (null)}
2016-03-05 00:46:44.109 NSOperation-Test[747:18521] ------I am op3 in queue3------<NSThread: 0x7b122120>{number = 4, name = (null)}
2016-03-05 00:46:44.109 NSOperation-Test[747:18523] ------I am op1 in queue1------<NSThread: 0x7b227d60>{number = 3, name = (null)}
2016-03-05 00:46:44.109 NSOperation-Test[747:18585] ------I am op2 in queue2------<NSThread: 0x7b122600>{number = 5, name = (null)}
2016-03-05 00:46:45.555 NSOperation-Test[747:18521] ------I am op2 in queue2------<NSThread: 0x7b122120>{number = 4, name = (null)}
2016-03-05 00:46:45.555 NSOperation-Test[747:18523] ------I am op1 in queue1------<NSThread: 0x7b227d60>{number = 3, name = (null)}
2016-03-05 00:46:45.556 NSOperation-Test[747:18522] ------I am op3 in queue3------<NSThread: 0x7a66f630>{number = 2, name = (null)}
2016-03-05 00:46:47.417 NSOperation-Test[747:18522] ------I am op1 in queue1------<NSThread: 0x7a66f630>{number = 2, name = (null)}
2016-03-05 00:46:47.417 NSOperation-Test[747:18523] ------I am op2 in queue2------<NSThread: 0x7b227d60>{number = 3, name = (null)}
2016-03-05 00:46:47.417 NSOperation-Test[747:18585] ------I am op3 in queue3------<NSThread: 0x7b122600>{number = 5, name = (null)}
2016-03-05 00:46:49.218 NSOperation-Test[747:18522] ------I am op1 in queue1------<NSThread: 0x7a66f630>{number = 2, name = (null)}
2016-03-05 00:46:49.219 NSOperation-Test[747:18523] ------I am op3 in queue3------<NSThread: 0x7b227d60>{number = 3, name = (null)}
2016-03-05 00:46:49.218 NSOperation-Test[747:18585] ------I am op2 in queue2------<NSThread: 0x7b122600>{number = 5, name = (null)}

 

可以看到并没有按照依赖关系的顺序执行,所以说关系必须在操作放入队列之前需要提前设定。

 

以上是关于[New learn] NSOperation基本使用的主要内容,如果未能解决你的问题,请参考以下文章

NSOperation

[New Learn] RunLoop学习-官方译文

iOS开发多线程篇 10 —NSOperation基本操作

ios开发多线程一:了解-NSOperation的基本使用

多线程 NSOperation

3.多线程NSOperation