iOS八线程费用之NSOperation

1. NSOperation简介

NSOperation
是苹果提供给我们的一套二十八线程化解方案。实际上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是苹果提供的一套八线程消除方案。实际上NSOperation是基于GCD更加高级中学一年级层的包裹,不过比GCD更加的面向对象、代码可读性越来越高、可控性越来越强,相当的屌的是加盟了操作注重。

      暗中同意景况下,NSOperation单独选择时只能同步实施操作,并未有开垦新线程的力量,独有合作NSOperationQueue手艺达成异步实行。讲到这里,我们轻易窥见GCD和NSOperation完结的艺术很像,其实那更疑似废话,NSOperation自个儿就是依赖GCD的包装,NSOperation相当于GCD中的职责,而NSOperationQueue则相当于GCD中的队列,前边《iOS八线程支付之GCD(上篇)》中一度演说过GCD的本来面目:开垦者要做的只是概念想举行的任务并追加到合适的Dispatch
Queue中。那样大家也可说NSOperation的本质便是:定义想举办的职务(NSOperation)并追加到至极的NSOperationQueue中。

 

2. NSOperation的宗旨选用

二、NSOperation使用

     1、创造任务

   
 NSOperation是七个虚幻的基类,表示四个独门的持筹握算单元,可感到子类提供有用且线程安全的树立情状,优先级,注重和撤除等操作。但它不可能向来用来封装义务,只可以通过它的子类来封装,一般的大家得以应用: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还提供了贰个方法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的天职在新开线程中实行。

   

    (3)自定义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}

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

 

    2、创立队列

 
  NSOperationQueue一齐有三种队列:主队列、其余队列。当中任何队列同期涵盖了串行、并发效率,通过安装最大并发数maxConcurrentOperationCount来贯彻串行、并发!

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

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];

    (2)其余队列 — 职分在子线程中施行

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

 

    3、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)}

    解析:开启新线程,并发推行。

 

2.1 成立职务

NSOperation是个抽象类,并不可能封装职分。我们独有利用它的子类来封装职务,大家有三种方法来封装任务。

a. 使用子类NSInvocationOperation

b. 使用子类NSBlockOperation

c. 定义承接自NSOperation的子类,通过完毕内部相应的议程来封装职务。

在不应用NSOperationQueue,单独行使NSOperation的情状下系统一齐实行操作,上边大家询问一下:

三、NSOperationQueue管理

     1、队列的裁撤、暂停、恢复生机

         – (void)cancel;
                          NSOperation提供的艺术,可打消单个操作

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

         –
(void)setSuspended:(BOOL)b;  
 可安装职务的暂停和还原,YES代表暂停队列,NO代表复苏队列

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

       
 暂停或吊销并不可能使正在实践的操作立刻暂停或撤销,而是当前操作实践完后不再推行新的操作。两者的差别在于暂停操作之后还能回复操作,继续向下进行;而撤回操作之后,全数的操作就清空了,无法再跟着执行剩下的操作。

 

     2、最大并发数 maxConcurrentOperationCount

          maxConcurrentOperationCount = -
1  表示不限定,暗中同意并发实行;

          maxConcurrentOperationCount =
1 意味着最大并发数为1,串行实施;

          maxConcurrentOperationCount >
([count] > =1)  代表并发实践,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轻易多了!

     

     3、操作依赖

   
  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)}

    深入分析:task2一定在task1前面实践,因为实行task1前设置了线程等待2s,全体task3最早试行。

 

    4、操作优先级

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

   

    5、操作的监听

     
 能够监听一个操作是不是进行实现,如下载图片,须求在下载第一张图片后才具下载第二张图纸,这里就足以设置监听。

- (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中的加多依赖。

 

   
留在最终的话:十六线程不只是有GCD!即使你还不曾用过NSOperation,还说什么样啊?赶紧演练起来!当然他们各有各的运用意况,存在即成立!iOS八线程的二种技术GCD、NSThread、NSOperation就都介绍完了,须要通晓 GCD、NSThread的能够回头看看作者事先的博客。 

   

2.2 使用子类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.4 自定义继承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
一共有三种队列:主队列、别的队列。个中任何队列相同的时候涵盖了串行、并发效能。上面是主队列、别的队列的主导创造方法和特色。

– 主队列:

    凡是增添到主队列中的职务(NSOperation),都会安置主线程中实行

NSOperationQueue *queue = [NSOperationQueue mainQueue];

 – 别的队列(非主队列)

    · 增添到这种队列中的职务(NSOperation),就能够自动放到子线程中实施

    · 同一时间含有了:串行、并发功效

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

 4. 将任务出席到行列中

前面说了,NSOperation须求相称NSOperationQueue来落成十六线程,那么大家必要将成立好的天职加入到行列中去,总共有两种艺术:

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)}

 大家能够看到,都以在新的线程中推行的,并且是出新推行。

5. 说了算串行实践和并行实践的基本点

NSOperationQueue创造的任何队列同期负有串行、并发作用,上面展现了并发功用,那么她的串行作用是怎么落实的?

此地就需求使用一个器重参数:maxConcurrentOperationCount –
最大并发数。约等于最大还要施行的天职位数量,一般2-3,大于5未来,尽管是在子线程中管理,可是cpu管理过多的子线程,也会有望影响主线程。

  • 暗许景况下为 -1,表示不进行限制,默感觉并发试行
  • 当maxConcurrentOperationCount为1时,进行串行实践。
  • 当maxConcurrentOperationCount大于1时,实行并发施行,当然那么些值不应抢先系统限制,不然正是本人安装一个相当的大的值,系统也会活动调治。

或然刚刚这些例子,大家只设置NSOperationQueue的最大并发数为1

 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)}

 能够阅览,当最大并发数为1的时候,职分是按顺序串行试行的,当最大并发数为2时,职分是出新实施的,何况展开线程数量是由系统调控的。

6. 操作信赖

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;

 注意:


这里的中断和打消并不表示能够将日前的操作立时打消,而是当当前的操作试行实现后不复推行新的操作。


暂停和注销的界别在于:暂停操作之后还能够复苏操作,继续向下实行;而撤回操作之后,全体的操作就清空了,不能够再接着推行剩下的操作。

相关文章