iOS10贰线程开销之NSOperation爱博体育

1. NSOperation简介

NSOperation
是苹果提必要我们的一套二10多线程消除方案。实际上NSOperation是依据GCD更加高一层的包裹,但是比GCD更简约易用、代码可读性也越来越高。

NSOperation供给相称NSOperationQueue来达成二拾三十二线程。因为在暗许情状下,NSOperation单独采纳时系统一起试行操作,并不曾开辟新线程的手艺,唯有NSOperationQueue本事实现异步推行。

NSOperation是依照GCD的,那么使用起来也和GCD差不离,当中,NSOperation约等于GCD中的职责,而NSOperationQueue则也正是GCD中的队列。NSOperation完毕四线程的施用手续分为三步:

  • 始建任务:先将索要执行的操作封装到一个NSOperation对象中;
  • 成立队列:创造NSOperationQueue对象;
  • 将职务插足到行列中:然后将NSOperation对象增多到NSOperationQueue中。

从此现在,系统就能够自行将NSOperationQueue中的NSOperation抽取来,在偃师市城中施行操作。

– – – – – – – – –
下边大家来学习下NSOperation和NSOperationQueue的主导采纳 – – – – – – 

一、什么是NSOperation?

     
NSOperation是苹果提供的1套多线程化解方案。实际上NSOperation是依据GCD越来越高1层的包装,不过比GCD越来越面向对象、代码可读性越来越高、可控性越来越强,很厉害的是出席了操作注重。

      私下认可境况下,NSOperation单独接纳时不得差别步实行操作,并未开荒新线程的技能,只有合营NSOperationQueue本领达成异步推行。讲到这里,我们简单察觉GCD和NSOperation达成的点子很像,其实那更像是废话,NSOperation本人正是依据GCD的包裹,NSOperation也正是GCD中的义务,而NSOperationQueue则相当于GCD中的队列,前边《爱博体育,iOS拾二线程成本之GCD(上篇)》中早已演讲过GCD的本质:开荒者要做的只是概念想进行的天职并增添到1二分的Dispatch
Queue中。那样我们也可说NSOperation的真相正是:定义想进行的天职(NSOperation)并增添到非常的NSOperationQueue中。

 

2. NSOperation的主导选取

二、NSOperation使用

     一、创建任务

   
 NSOperation是2个虚幻的基类,表示贰个独立的持筹握算单元,可以为子类提供有用且线程安全的树立景况,优先级,信赖和撤回等操作。但它不可能向来用来封装任务,只好通过它的子类来封装,一般的我们得以选用:NSBlockOperation、NSInvocationOperation恐怕定义承袭自NSOperation的子类,通过落到实处内部相应的法子来封装职责。

   (1)NSInvocationOperation

- (void)invocationOperation{

    NSLog(@"start - %@",[NSThread currentThread]);

    // 创建NSInvocationOperation对象
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(testRun) object:nil];

    // 调用start方法开始执行操作
    [op start];

    NSLog(@"end - %@",[NSThread currentThread]);
}

- (void)testRun{
    NSLog(@"invocationOperation -- %@", [NSThread currentThread]);
}

    执行结果:

2017-07-14 13:43:59.327 beck.wang[10248:1471363] start - <NSThread: 0x6100000614c0>{number = 1, name = main}
2017-07-14 13:43:59.328 beck.wang[10248:1471363] invocationOperation -- <NSThread: 0x6100000614c0>{number = 1, name = main}
2017-07-14 13:43:59.328 beck.wang[10248:1471363] end - <NSThread: 0x6100000614c0>{number = 1, name = main}

    解析:单独采纳NSInvocationOperation的情状下,NSInvocationOperation在主线程同步推行操作,并不曾拉开新线程。

  (2)NSBlockOperation

- (void)blockOperation{

    NSLog(@"start - %@",[NSThread currentThread]);

    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{

        NSLog(@"blockOperation--%@", [NSThread currentThread]);
    }];

    NSLog(@"end - %@",[NSThread currentThread]);

    [op start];
}

    打字与印刷结果:

2017-07-14 13:49:25.436 beck.wang[10304:1476355] start - <NSThread: 0x6100000653c0>{number = 1, name = main}
2017-07-14 13:49:25.436 beck.wang[10304:1476355] end - <NSThread: 0x6100000653c0>{number = 1, name = main}
2017-07-14 13:49:25.436 beck.wang[10304:1476355] blockOperation--<NSThread: 0x6100000653c0>{number = 1, name = main}

    深入分析:单独使用NSBlockOperation的情状下,NSBlockOperation也是在主线程施行操作,未有开启新线程。

   
值得注意的是:NSBlockOperation还提供了3个艺术addExecutionBlock:,通过addExecutionBlock:就可以为NSBlockOperation加多额外的操作,这么些额外的操作就能够在此外线程并发实践。

- (void)blockOperation{

    NSLog(@"start - %@",[NSThread currentThread]);

    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{

        NSLog(@"blockOperation--%@", [NSThread currentThread]);
    }];

    // 添加额外任务(在子线程执行)
    [op addExecutionBlock:^{
        NSLog(@"addTask1---%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"addTask2---%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"addTask3---%@", [NSThread currentThread]);
    }];

    NSLog(@"end - %@",[NSThread currentThread]);

    [op start];
}

     打字与印刷结果:

2017-07-14 13:57:02.009 beck.wang[10351:1482603] start - <NSThread: 0x60000007cdc0>{number = 1, name = main}
2017-07-14 13:57:02.009 beck.wang[10351:1482603] end - <NSThread: 0x60000007cdc0>{number = 1, name = main}
2017-07-14 13:57:02.010 beck.wang[10351:1482603] blockOperation--<NSThread: 0x60000007cdc0>{number = 1, name = main}
2017-07-14 13:57:02.010 beck.wang[10351:1482642] addTask1---<NSThread: 0x618000260e00>{number = 3, name = (null)}
2017-07-14 13:57:02.010 beck.wang[10351:1482645] addTask3---<NSThread: 0x600000263200>{number = 5, name = (null)}
2017-07-14 13:57:02.010 beck.wang[10351:1482643] addTask2---<NSThread: 0x610000264600>{number = 4, name = (null)}

    剖析:blockOperationWithBlock任务在主线程中施行,addExecutionBlock的任务在新开线程中施行。

   

    (三)自定义NSOperation子类--重写main方法就能够

    .h

@interface ZTOperation : NSOperation

@end

    .m

@implementation ZTOperation

- (void)main{

    // 在这里可以自定义任务
    NSLog(@"ZTOperation--%@",[NSThread currentThread]);
}
@end

    ViewController

ZTOperation *zt = [[ZTOperation alloc] init];
[zt start];

    打字与印刷结果:

2017-07-14 14:05:58.824 beck.wang[10389:1490955] ZTOperation--<NSThread: 0x60000007a940>{number = 1, name = main}

    分析:职务在主线程中实行,不开启新线程。

 

    二、创造队列

 
  NSOperationQueue总共有三种队列:主队列、别的队列。个中任何队列同时涵盖了串行、并发功效,通过设置最大并发数maxConcurrentOperationCount来贯彻串行、并发!

  (1)主队列  — 职务在主线程中实施

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];

    (贰)其余队列 — 职分在子线程中实行

NSOperationQueue *elseQueue = [[NSOperationQueue alloc] init];

 

    三、NSOperation  +  NSOperationQueue (职分加多到行列)

// 添加单个操作:
 - (void)addOperation:(NSOperation *)op;

// 添加多个操作:
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait NS_AVAILABLE(10_6, 4_0);

// 添加block操作:
- (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);

      代码示例:

- (void)addOperationToQueue
{

    NSLog(@"start - %@",[NSThread currentThread]);

    // 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 创建NSInvocationOperation
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(testRun) object:nil];

    // 创建NSBlockOperation
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"task002 -- %@", [NSThread currentThread]);
    }];

    // 添加操作到队列中: addOperation:
    [queue addOperation:op1];
    [queue addOperation:op2];

    // 添加操作到队列中:addOperationWithBlock:
    [queue addOperationWithBlock:^{
        NSLog(@"task003-----%@", [NSThread currentThread]);
    }];

    NSLog(@"end - %@",[NSThread currentThread]);
}

- (void)testRun{
    NSLog(@"task001 -- %@", [NSThread currentThread]);
}

     打字与印刷结果:

2017-07-14 14:39:51.669 beck.wang[10536:1516641] start - <NSThread: 0x610000077640>{number = 1, name = main}
2017-07-14 14:39:51.670 beck.wang[10536:1516641] end - <NSThread: 0x610000077640>{number = 1, name = main}
2017-07-14 14:39:51.670 beck.wang[10536:1516686] task003-----<NSThread: 0x600000077200>{number = 3, name = (null)}
2017-07-14 14:39:51.670 beck.wang[10536:1516689] task002 -- <NSThread: 0x61800007e080>{number = 5, name = (null)}
2017-07-14 14:39:51.670 beck.wang[10536:1516687] task001 -- <NSThread: 0x61000007e1c0>{number = 4, name = (null)}

    拆解分析:开启新线程,并发施行。

 

贰.1 创制职分

NSOperation是个抽象类,并不可能封装职务。大家只有应用它的子类来封装任务,大家有两种办法来封装职分。

a. 使用子类NSInvocationOperation

b. 使用子类NSBlockOperation

c. 定义承继自NSOperation的子类,通过落实内部相应的措施来封装义务。

在不选择NSOperationQueue,单独选取NSOperation的景况下系统同步推行操作,上边大家领会一下:

三、NSOperationQueue管理

     一、队列的吊销、暂停、复苏

         – (void)cancel;
                          NSOperation提供的法子,可收回单个操作

         –
(void)cancelAllOperations;        
NSOperationQueue提供的点子,能够收回队列的拥有操作

         –
(void)setSuspended:(BOOL)b;  
 可安装任务的中断和回复,YES代表暂停队列,NO代表复苏队列

         –
(BOOL)isSuspended;                剖断暂停状态

       
 暂停或撤销并不能够使正在施行的操作立刻暂停或注销,而是当前操作施行完后不再实行新的操作。两个的分裂在于暂停操作之后还足以过来操作,继续向下实践;而打消操作之后,全体的操作就清空了,无法再接着实施剩下的操作。

 

     2、最大并发数 maxConcurrentOperationCount

          maxConcurrentOperationCount = -
1  表示不限制,默许并发奉行;

          maxConcurrentOperationCount =
1 代表最大并发数为一,串行实践;

          maxConcurrentOperationCount >
([count] > =一)  代表并发推行,min[count,系统限制]。

         代码示例:

- (void)operationQueue
{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 设置最大并发操作数
    // queue.maxConcurrentOperationCount = - 1;  // 并发执行
    // queue.maxConcurrentOperationCount = 1; // 同步执行
     queue.maxConcurrentOperationCount = 2; // 并发执行

    [queue addOperationWithBlock:^{
        NSLog(@"task1-----%@", [NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"task2-----%@", [NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"task3-----%@", [NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"task4-----%@", [NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"task5-----%@", [NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"task6-----%@", [NSThread currentThread]);
    }];
}

     打印结果:

// queue.maxConcurrentOperationCount = - 1

2017-07-14 15:28:39.554 beck.wang[10772:1557342] task2-----<NSThread: 0x61800006d340>{number = 4, name = (null)}
2017-07-14 15:28:39.554 beck.wang[10772:1557358] task3-----<NSThread: 0x6080000751c0>{number = 5, name = (null)}
2017-07-14 15:28:39.554 beck.wang[10772:1557359] task4-----<NSThread: 0x610000071c00>{number = 6, name = (null)}
2017-07-14 15:28:39.554 beck.wang[10772:1557339] task5-----<NSThread: 0x60000006ea40>{number = 7, name = (null)}
2017-07-14 15:28:39.554 beck.wang[10772:1557340] task1-----<NSThread: 0x608000073500>{number = 3, name = (null)}
2017-07-14 15:28:39.554 beck.wang[10772:1557360] task6-----<NSThread: 0x610000071c80>{number = 8, name = (null)}

// 分析:线程数为6,并发执行

-----------------------------------分割线----------------------------------------------

// queue.maxConcurrentOperationCount =  1

2017-07-14 15:27:04.365 beck.wang[10743:1555231] task1-----<NSThread: 0x60800007c880>{number = 3, name = (null)}
2017-07-14 15:27:04.365 beck.wang[10743:1555231] task2-----<NSThread: 0x60800007c880>{number = 3, name = (null)}
2017-07-14 15:27:04.365 beck.wang[10743:1555231] task3-----<NSThread: 0x60800007c880>{number = 3, name = (null)}
2017-07-14 15:27:04.365 beck.wang[10743:1555231] task4-----<NSThread: 0x60800007c880>{number = 3, name = (null)}
2017-07-14 15:27:04.366 beck.wang[10743:1555231] task5-----<NSThread: 0x60800007c880>{number = 3, name = (null)}
2017-07-14 15:27:04.366 beck.wang[10743:1555231] task6-----<NSThread: 0x60800007c880>{number = 3, name = (null)}

// 分析:线程个数为1,同步执行

-----------------------------------分割线----------------------------------------------

// queue.maxConcurrentOperationCount =  2

2017-07-14 15:18:26.162 beck.wang[10715:1548342] task2-----<NSThread: 0x608000079740>{number = 4, name = (null)}
2017-07-14 15:18:26.162 beck.wang[10715:1548344] task1-----<NSThread: 0x6100000770c0>{number = 3, name = (null)}
2017-07-14 15:18:26.162 beck.wang[10715:1548342] task4-----<NSThread: 0x608000079740>{number = 4, name = (null)}
2017-07-14 15:18:26.162 beck.wang[10715:1548344] task3-----<NSThread: 0x6100000770c0>{number = 3, name = (null)}
2017-07-14 15:18:26.162 beck.wang[10715:1548342] task5-----<NSThread: 0x608000079740>{number = 4, name = (null)}
2017-07-14 15:18:26.163 beck.wang[10715:1548344] task6-----<NSThread: 0x6100000770c0>{number = 3, name = (null)}

// 分析:线程个数为2,并发执行

     很醒目,通过安装maxConcurrentOperationCount就能够落到实处产出、串行功用是否比GCD轻便多了!

     

     三、操作注重

   
  NSOperation中大家得认为操作分解为多少个小的职务,通过丰硕他们之间的依附关系张开操作,那个平日用到!那也是NSOperation吸引人的地点,不供给像GCD那样选择复杂的代码达成,addDependency就能够解决!

- (void)addDependency
{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        sleep(2);
        NSLog(@"task1-----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"task2-----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"task3-----%@", [NSThread  currentThread]);
    }];

    // op2依赖于op1 执行顺序op1->op2 必须放在[添加操作队列]之前
    [op2 addDependency:op1];

    // 忌循环依赖 op2已经依赖于op1,切不可再让op1依赖于op2,形成循环依赖
    //[op1 addDependency:op2];

    // 添加操作队列
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
}

     打印结果:

2017-07-14 15:46:02.011 beck.wang[10854:1571574] task3-----<NSThread: 0x61800006d740>{number = 3, name = (null)}
2017-07-14 15:46:04.085 beck.wang[10854:1571596] task1-----<NSThread: 0x60000006f040>{number = 4, name = (null)}
2017-07-14 15:46:04.085 beck.wang[10854:1571574] task2-----<NSThread: 0x61800006d740>{number = 3, name = (null)}

    分析:task二一定在task壹前边实施,因为实行task1前安装了线程等待2s,全部task三最早实行。

 

    四、操作优先级

NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8

   

    5、操作的监听

     
 能够监听1个操作是不是实施达成,如下载图片,需求在下载第二张图片后才干下载第一张图纸,这里就能够安装监听。

- (void)addListing{

    NSOperationQueue *queue=[[NSOperationQueue alloc]init];

    NSBlockOperation *operation=[NSBlockOperation blockOperationWithBlock:^{
        for (int i=0; i<3; i++) {
            NSLog(@"下载图片1-%@",[NSThread currentThread]);
        }
    }];

    // 监听操作的执行完毕
    operation.completionBlock=^{
        // 继续进行下载图片操作
        NSLog(@"--下载图片2--");
    };

    [queue addOperation:operation];
}

     推行结果:

2017-07-14 16:21:43.833 beck.wang[10930:1597954] 下载图片1-<NSThread: 0x61800007a340>{number = 3, name = (null)}
2017-07-14 16:21:43.834 beck.wang[10930:1597954] 下载图片1-<NSThread: 0x61800007a340>{number = 3, name = (null)}
2017-07-14 16:21:43.834 beck.wang[10930:1597954] 下载图片1-<NSThread: 0x61800007a340>{number = 3, name = (null)}
2017-07-14 16:21:43.834 beck.wang[10930:1597955] --下载图片2--

    深入分析:下载图片1达成后才会施行下载图片2,这里就好像知识点3中的增添信赖。

 

   
留在最终的话:10贰线程不只是有GCD!假诺您还并未有用过NSOperation,还说哪些啊?赶紧演习起来!当然他们各有各的使用情形,存在即创制!iOS三八线程的两种技术GCD、NSThread、NSOperation就都介绍完了,要求明白 GCD、NSThread的能够回头看看本人事先的博客。 

   

二.贰 使用子类NSInvocationOperation

// 1.创建NSInvocationOperation对象
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];

// 2.调用start方法开始执行操作
[op start];

- (void)run
{
    NSLog(@"------%@", [NSThread currentThread]);
}

 输出结果:

<NSThread: 0x604000260b00>{number = 1, name = main}

从中能够见到,在未曾选择NSOperationQueue,单独使用NSOperation的动静下,NSInvocationOperation是在近期线程中实施操作,并从未开创新的线程。因为,当前线程是主线程,所以打字与印刷出的是主线程。

2.3 使用NSBlockOperation

NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
    // 在主线程
    NSLog(@"------%@", [NSThread currentThread]);
}];

[op start];

出口结果:

<NSThread: 0x604000260b00>{number = 1, name = main}

 从中能够看到,在尚未选取NSOperationQueue,单独行使NSOperation的景观下,NSBlockOperation是在主线程中实行操作,并未开立异的线程。

而是NSBlockOperation还提供了二个艺术
addExecutionBlock:,通过那几个格局就能够为NSBlockOperation增添额外的操作,那几个额外的操作就能够在其他线程中并发推行。

出口结果

 NSOperation[1326:87276] 3 - <NSThread: 0x6000002715c0>{number = 4, name = (null)}
 NSOperation[1326:87282] 1 - <NSThread: 0x604000271780>{number = 3, name = (null)}
 NSOperation[1326:87200] <NSThread: 0x60000006c6c0>{number = 1, name = main}
 NSOperation[1326:87274] 2 - <NSThread: 0x6000002714c0>{number = 5, name = (null)}

 大家得以看到,职责都以出新施行的,NSBlockOperation中的职分是在主线程中实践的,addExecutionBlock是在新线程中进行的。

2.四 自定义承接NSOperation的子类

WCEOperation.h

#import <Foundation/Foundation.h>

@interface WCEOperation : NSOperation

@end

 WCEOperation.m

#import "WCEOperation.h"

@implementation WCEOperation
// 需要执行的任务
-(void)main{
    for (NSInteger i = 0; i < 5; i++) {
        NSLog(@"%@",[NSThread currentThread]);
    }
}

@end

 在接纳的时候,导入那几个文件

WCEOperation *operation = [[WCEOperation alloc] init];
[operation start];

 输出结果:

2017-12-11 22:53:42.126565+0800 NSOperation[1388:90762] <NSThread: 0x60400007fe40>{number = 1, name = main}
2017-12-11 22:53:42.126803+0800 NSOperation[1388:90762] <NSThread: 0x60400007fe40>{number = 1, name = main}
2017-12-11 22:53:42.126946+0800 NSOperation[1388:90762] <NSThread: 0x60400007fe40>{number = 1, name = main}
2017-12-11 22:53:42.127171+0800 NSOperation[1388:90762] <NSThread: 0x60400007fe40>{number = 1, name = main}
2017-12-11 22:53:42.127309+0800 NSOperation[1388:90762] <NSThread: 0x60400007fe40>{number = 1, name = main}

 大家能够看出,在未曾选取NSOperationQueue的动静下,职责都以在主线程中实施的,并未有拉开新的线程。

3. NSOperationQueue的主导使用

和GCD中的并发队列、串行队列略有分歧的是:NSOperationQueue
1共有二种队列:主队列、别的队列。个中任何队列同时含有了串行、并发功效。下面是主队列、其余队列的中央创设方法和特点。

– 主队列:

    凡是增多到主队列中的任务(NSOperation),都会停放主线程中实施

NSOperationQueue *queue = [NSOperationQueue mainQueue];

 – 别的队列(非主队列)

    · 增添到这种队列中的职责(NSOperation),就能活动放到子线程中实行

    · 同期涵盖了:串行、并发功用

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

 四. 将职分参与到行列中

前面说了,NSOperation须要合作NSOperationQueue来达成拾2线程,那么我们需求将开创好的天职参预到行列中去,总共有三种办法:

a: – (void)addOperation:(NSOperation *)op;

内需先创立职责,再将开创好的职责出席到成立好的队列中去

NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];

    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"%@ - 1",[NSThread currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"%@ - 2",[NSThread currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"%@ - 3",[NSThread currentThread]);
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"%@ - 4",[NSThread currentThread]);
    }];
    NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"%@ - 5",[NSThread currentThread]);
    }];
    [opQueue addOperation:op1];
    [opQueue addOperation:op2];
    [opQueue addOperation:op3];
    [opQueue addOperation:op4];
    [opQueue addOperation:op5];

 运维结果:

2017-12-11 23:07:19.203418+0800 NSOperation[1468:97665] <NSThread: 0x60000046ac80>{number = 4, name = (null)} - 4
2017-12-11 23:07:19.203429+0800 NSOperation[1468:97664] <NSThread: 0x604000463300>{number = 6, name = (null)} - 1
2017-12-11 23:07:19.203439+0800 NSOperation[1468:97666] <NSThread: 0x60000046a980>{number = 5, name = (null)} - 2
2017-12-11 23:07:19.203867+0800 NSOperation[1468:97665] <NSThread: 0x60000046ac80>{number = 4, name = (null)} - 5
2017-12-11 23:07:19.204450+0800 NSOperation[1468:97667] <NSThread: 0x604000463280>{number = 3, name = (null)} - 3

 可以看看,NSOperation和NSOperationQueue结合后能够打开新线程,并发试行。

b. – (void)addOperationWithBlock:(void(^)void)block;

不必创造职分,在block中丰裕职务,直接将职分block参与到行列中。

NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];

    [opQueue addOperationWithBlock:^{
        NSLog(@"1 - - %@",[NSThread currentThread]);
    }];
    [opQueue addOperationWithBlock:^{
        NSLog(@"2 - - %@",[NSThread currentThread]);
    }];
    [opQueue addOperationWithBlock:^{
        NSLog(@"3 - - %@",[NSThread currentThread]);
    }];
    [opQueue addOperationWithBlock:^{
        NSLog(@"4 - - %@",[NSThread currentThread]);
    }];
    [opQueue addOperationWithBlock:^{
        NSLog(@"5 - - %@",[NSThread currentThread]);
    }];

 运维结果:

2017-12-11 23:17:38.951881+0800 NSOperation[1510:102617] 2 - - <NSThread: 0x600000275400>{number = 4, name = (null)}
2017-12-11 23:17:38.951884+0800 NSOperation[1510:102615] 4 - - <NSThread: 0x6040004655c0>{number = 6, name = (null)}
2017-12-11 23:17:38.951889+0800 NSOperation[1510:102616] 1 - - <NSThread: 0x604000465540>{number = 3, name = (null)}
2017-12-11 23:17:38.951917+0800 NSOperation[1510:102619] 3 - - <NSThread: 0x604000465580>{number = 5, name = (null)}
2017-12-11 23:17:38.952182+0800 NSOperation[1510:102614] 5 - - <NSThread: 0x6000002754c0>{number = 7, name = (null)}

 大家能够见到,都以在新的线程中实践的,并且是出现实践。

伍. 说了算串行实行和并行实践的重视

NSOperationQueue创制的其余队列同一时间具备串行、并发功效,上边突显了并发成效,那么她的串行成效是什么落到实处的?

此处就须要利用3个最重要参数:maxConcurrentOperationCount –
最大并发数。也正是最大并且实践的职分数,一般2-3,大于伍后头,固然是在子线程中管理,不过cpu管理过多的子线程,也许有望影响主线程。

  • 默许情状下为 -一,表示不开始展览界定,默以为并发推行
  • 当maxConcurrentOperationCount为1时,实行串行施行。
  • 当maxConcurrentOperationCount大于1时,实行并发执行,当然那几个值不应超越系统限制,不然尽管本身设置1个比十分大的值,系统也会自行调解。

可能刚刚那么些例子,大家只设置NSOperationQueue的最大并发数为一

 NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];

    opQueue.maxConcurrentOperationCount = 1;

    [opQueue addOperationWithBlock:^{
        NSLog(@"1 - - %@",[NSThread currentThread]);
    }];
    [opQueue addOperationWithBlock:^{
        NSLog(@"2 - - %@",[NSThread currentThread]);
    }];
    [opQueue addOperationWithBlock:^{
        NSLog(@"3 - - %@",[NSThread currentThread]);
    }];
    [opQueue addOperationWithBlock:^{
        NSLog(@"4 - - %@",[NSThread currentThread]);
    }];
    [opQueue addOperationWithBlock:^{
        NSLog(@"5 - - %@",[NSThread currentThread]);
    }];

 输出结果:

2017-12-11 23:22:30.824859+0800 NSOperation[1535:105237] 1 - - <NSThread: 0x600000275cc0>{number = 3, name = (null)}
2017-12-11 23:22:30.825228+0800 NSOperation[1535:105238] 2 - - <NSThread: 0x600000275d00>{number = 4, name = (null)}
2017-12-11 23:22:30.825614+0800 NSOperation[1535:105238] 3 - - <NSThread: 0x600000275d00>{number = 4, name = (null)}
2017-12-11 23:22:30.826585+0800 NSOperation[1535:105250] 4 - - <NSThread: 0x6000002756c0>{number = 5, name = (null)}
2017-12-11 23:22:30.826851+0800 NSOperation[1535:105250] 5 - - <NSThread: 0x6000002756c0>{number = 5, name = (null)}

 能够看出,当最大并发数为一的时候,任务是按顺序串行奉行的,当最大并发数为2时,职责是出现实施的,而且展开线程数量是由系统调整的。

陆. 操作重视

NSOperation和NSOperationQueue最吸引认得地点是它能丰富操作之间的重视性关系。比方有A和B七个操作,个中A推行完操作,B能力实施操作,那么就要求让B依赖于A:

NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];

    NSBlockOperation *opA = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"我是opA,我先执行 - %@",[NSThread currentThread]);
    }];

    NSBlockOperation *opB = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"我是opB,我后执行 - %@",[NSThread currentThread]);
    }];

    [opB addDependency:opA];

    [opQueue addOperation:opB];
    [opQueue addOperation:opA];

 输出结果:

2017-12-11 23:31:23.494225+0800 NSOperation[1569:109435] 我是opA,我先执行 - <NSThread: 0x604000079ec0>{number = 3, name = (null)}
2017-12-11 23:31:23.494688+0800 NSOperation[1569:109434] 我是opB,我后执行 - <NSThread: 0x60000046f2c0>{number = 4, name = (null)}

 能够看出,无论运维几回,其结果都是opA先执行,opB后举办。注意:不能够opA依赖opB,同期opB依赖opA。职务实行的依次,并不取决于增加的次第,施行的依次取决于依赖。

7. 别的的点子

// NSOperation提供的方法,可取消单个操作
- (void)cancel;

// NSOperationQueue提供的方法,可以取消队列的所有操作
- (void)cancelAllOperations;

// 可以设置任务的暂停和恢复,YES代表暂停队列,No表示恢复队列。
- (void)setSuspended:(BOOL)b;

// 判断暂停状态
- (BOOL)isSuspended;

// 设置操作优先级 优先级越高,被调用的几率越大

- (NSOperationQueuePriority)queuePriority;
- (void)setQueuePriority:(NSOperationQueuePriority)p;

 注意:


这里的中断和撤销并不意味着可以将方今的操作立刻撤回,而是当当前的操作实行完结后不再进行新的操作。


暂停和收回的分别在于:暂停操作之后还足以恢复操作,继续向下实行;而撤回操作之后,全数的操作就清空了,无法再接着推行剩下的操作。

相关文章