一、什么是NSOperation?

NSOperation是苹果提供的一套多线程解决方案。实际上NSOperation是基于GCD更高一层的封装,但是比GCD更加的面向对象、代码可读性更高、可控性更强,很屌的是加入了操作依赖。

默认情况下,NSOperation单独使用时只能同步执行操作,并没有开辟新线程的能力,只有配合NSOperationQueue才能实现异步执行。讲到这里,我们不难发现GCD和NSOperation实现的方式很像,其实这更像是废话,NSOperation本身就是基于GCD的封装,NSOperation相当于GCD中的任务,而NSOperationQueue则相当于GCD中的队列,前面《iOS多线程开发之GCD(上篇)》中已经阐述过GCD的实质:开发者要做的只是定义想执行的任务并追加到适当的Dispatch Queue中。这样我们也可说NSOperation的本质就是:定义想执行的任务(NSOperation)并追加到适当的NSOperationQueue中。

      使用 NSOperation、NSOperationQueue能带来的好处:

1.可添加完成的代码块,在操作完成后执行。

2.添加操作之间的依赖关系,方便的控制执行顺序。

3.设定操作执行的优先级(并不能改变依赖关系,只是修改已经确定的operation执行顺序,依赖关系优先)。

4.可以很方便的取消一个操作的执行。

5.使用 KVO 观察对操作执行状态的更改:isExecuteing、isFinished、isCancelled。

二、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]);
}

执行结果:

-- ::59.327 beck.wang[:] start - <NSThread: 0x6100000614c0>{number = , name = main}
-- ::59.328 beck.wang[:] invocationOperation -- <NSThread: 0x6100000614c0>{number = , name = main}
-- ::59.328 beck.wang[:] end - <NSThread: 0x6100000614c0>{number = , 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];
}

打印结果:

-- ::25.436 beck.wang[:] start - <NSThread: 0x6100000653c0>{number = , name = main}
-- ::25.436 beck.wang[:] end - <NSThread: 0x6100000653c0>{number = , name = main}
-- ::25.436 beck.wang[:] blockOperation--<NSThread: 0x6100000653c0>{number = , 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];
}

打印结果:

-- ::02.009 beck.wang[:] start - <NSThread: 0x60000007cdc0>{number = , name = main}
-- ::02.009 beck.wang[:] end - <NSThread: 0x60000007cdc0>{number = , name = main}
-- ::02.010 beck.wang[:] blockOperation--<NSThread: 0x60000007cdc0>{number = , name = main}
-- ::02.010 beck.wang[:] addTask1---<NSThread: 0x618000260e00>{number = , name = (null)}
-- ::02.010 beck.wang[:] addTask3---<NSThread: 0x600000263200>{number = , name = (null)}
-- ::02.010 beck.wang[:] addTask2---<NSThread: 0x610000264600>{number = , 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];

打印结果:

-- ::58.824 beck.wang[:] ZTOperation--<NSThread: 0x60000007a940>{number = , 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]);
}

打印结果:

-- ::51.669 beck.wang[:] start - <NSThread: 0x610000077640>{number = , name = main}
-- ::51.670 beck.wang[:] end - <NSThread: 0x610000077640>{number = , name = main}
-- ::51.670 beck.wang[:] task003-----<NSThread: 0x600000077200>{number = , name = (null)}
-- ::51.670 beck.wang[:] task002 -- <NSThread: 0x61800007e080>{number = , name = (null)}
-- ::51.670 beck.wang[:] task001 -- <NSThread: 0x61000007e1c0>{number = , name = (null)}

分析:开启新线程,并发执行。

三、NSOperationQueue管理

1、队列的取消、暂停、恢复

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

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

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

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

暂停或取消并不能使正在执行的操作立即暂停或取消,而是当前操作执行完后不再执行新的操作。两者的区别在于暂停操作之后还可以恢复操作,继续向下执行;而取消操作之后,所有的操作就清空了,无法再接着执行剩下的操作。

 取消操作知识延伸:

对于一个自定义NSOperation来说,想要取消操作,除了需要向其发送cancel消息之外,还必须手动实现某些方法。

         start和main方法中,都必须周期性的对cancelled属性进行判断,如果YES,则立即退出当前,终止操作。 其中,start方法中的判断使得尚未开始的操作及时退出; main方法中的判断使得正在执行当中的操作退出。
         start--检查是否取消-->main--检查是否取消

  • 取消单一操作:cancel / NSOperation

  • 取消队列中的操作:cancelAllOperations / NSOperationQueue

  • 取消正在执行的操作:需要在main方法中周期性的检查cancelled状态
/**
在合适的时机检查cancelled状态,退出当前任务.
三个切入点:
1. 开始执行任务之前
2. 每个for循环中
3. 阶段性的任务之间
*/
- (void)main
{
// 正式开始执行任务之前检查
if (self.isCancelled) {
return;
} for (int i = ; i < ; i++) {
// 每个循环开始之前检查
if (self.isCancelled) {
NSLog(@"退出当前任务");
return;
} // 一个漫长的任务
} // 阶段性任务之间检查
[self processLongTask];
}
  • 取消位于队列中等待执行的操作:start方法的默认实现会检查cancelled状态,如果为YES,则立即退出,不会调用main方法。

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 = ; // 并发执行 [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

-- ::39.554 beck.wang[:] task2-----<NSThread: 0x61800006d340>{number = , name = (null)}
-- ::39.554 beck.wang[:] task3-----<NSThread: 0x6080000751c0>{number = , name = (null)}
-- ::39.554 beck.wang[:] task4-----<NSThread: 0x610000071c00>{number = , name = (null)}
-- ::39.554 beck.wang[:] task5-----<NSThread: 0x60000006ea40>{number = , name = (null)}
-- ::39.554 beck.wang[:] task1-----<NSThread: 0x608000073500>{number = , name = (null)}
-- ::39.554 beck.wang[:] task6-----<NSThread: 0x610000071c80>{number = , name = (null)} // 分析:线程数为6,并发执行 -----------------------------------分割线---------------------------------------------- // queue.maxConcurrentOperationCount = 1 -- ::04.365 beck.wang[:] task1-----<NSThread: 0x60800007c880>{number = , name = (null)}
-- ::04.365 beck.wang[:] task2-----<NSThread: 0x60800007c880>{number = , name = (null)}
-- ::04.365 beck.wang[:] task3-----<NSThread: 0x60800007c880>{number = , name = (null)}
-- ::04.365 beck.wang[:] task4-----<NSThread: 0x60800007c880>{number = , name = (null)}
-- ::04.366 beck.wang[:] task5-----<NSThread: 0x60800007c880>{number = , name = (null)}
-- ::04.366 beck.wang[:] task6-----<NSThread: 0x60800007c880>{number = , name = (null)} // 分析:线程个数为1,同步执行 -----------------------------------分割线---------------------------------------------- // queue.maxConcurrentOperationCount = 2 -- ::26.162 beck.wang[:] task2-----<NSThread: 0x608000079740>{number = , name = (null)}
-- ::26.162 beck.wang[:] task1-----<NSThread: 0x6100000770c0>{number = , name = (null)}
-- ::26.162 beck.wang[:] task4-----<NSThread: 0x608000079740>{number = , name = (null)}
-- ::26.162 beck.wang[:] task3-----<NSThread: 0x6100000770c0>{number = , name = (null)}
-- ::26.162 beck.wang[:] task5-----<NSThread: 0x608000079740>{number = , name = (null)}
-- ::26.163 beck.wang[:] task6-----<NSThread: 0x6100000770c0>{number = , name = (null)} // 分析:线程个数为2,并发执行

很明显,通过设置maxConcurrentOperationCount就能实现并发、串行功能是不是比GCD轻松多了!

3、操作依赖

NSOperation中我们可以为操作分解为若干个小的任务,通过添加他们之间的依赖关系进行操作,这个经常用到!这也是NSOperation吸引人的地方,不需要像GCD那样使用复杂的代码实现,addDependency就可以搞定!

- (void)addDependency
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
sleep();
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];
}
     打印结果:

-- ::02.011 beck.wang[:] task3-----<NSThread: 0x61800006d740>{number = , name = (null)}
-- ::04.085 beck.wang[:] task1-----<NSThread: 0x60000006f040>{number = , name = (null)}
-- ::04.085 beck.wang[:] task2-----<NSThread: 0x61800006d740>{number = , name = (null)}

分析:task2一定在task1后面执行,因为执行task1前设置了线程等待2s,所有task3最早执行。

4、操作优先级

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

5、操作的监听

可以监听一个操作是否执行完毕,如下载图片,需要在下载第一张图片后才能下载第二张图片,这里就可以设置监听。

- (void)addListing{

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

    NSBlockOperation *operation=[NSBlockOperation blockOperationWithBlock:^{
for (int i=; i<; i++) {
NSLog(@"下载图片1-%@",[NSThread currentThread]);
}
}]; // 监听操作的执行完毕
operation.completionBlock=^{
// 继续进行下载图片操作
NSLog(@"--下载图片2--");
}; [queue addOperation:operation];
}

执行结果:

-- ::43.833 beck.wang[:] 下载图片1-<NSThread: 0x61800007a340>{number = , name = (null)}
-- ::43.834 beck.wang[:] 下载图片1-<NSThread: 0x61800007a340>{number = , name = (null)}
-- ::43.834 beck.wang[:] 下载图片1-<NSThread: 0x61800007a340>{number = , name = (null)}
-- ::43.834 beck.wang[:] --下载图片2--

分析:下载图片1完成后才会执行下载图片2,这里类似知识点3中的添加依赖。

iOS多线程开发之NSOperation - 快上车,没时间解释了!的更多相关文章

  1. iOS多线程开发之NSOperation

    一.什么是NSOperation? NSOperation是苹果提供的一套多线程解决方案.实际上NSOperation是基于GCD更高一层的封装,但是比GCD更加的面向对象.代码可读性更高.可控性更强 ...

  2. iOS多线程开发之GCD(中篇)

    前文回顾: 上篇博客讲到GCD的实现是由队列和任务两部分组成,其中获取队列的方式有两种,第一种是通过GCD的API的dispatch_queue_create函数生成Dispatch Queue:第二 ...

  3. iOS多线程开发之GCD(死锁篇)

    上篇和中篇讲解了什么是GCD,如何使用GCD,这篇文章将讲解使用GCD中将遇到的死锁问题.有兴趣的朋友可以回顾<iOS多线程开发之GCD(上篇)>和<iOS多线程开发之GCD(中篇) ...

  4. iOS多线程开发之GCD(中级篇)

    前文回顾: 上篇博客讲到GCD的实现是由队列和任务两部分组成,其中获取队列的方式有两种,第一种是通过GCD的API的dispatch_queue_create函数生成Dispatch Queue:第二 ...

  5. iOS多线程开发之GCD(基础篇)

    总纲: GCD基本概念 GCD如何实现 GCD如何使用 队列和任务组合 一.GCD基本概念 GCD 全称Grand Central Dispatch(大中枢队列调度),是一套低层API,提供了⼀种新的 ...

  6. iOS 多线程开发之OperationQueue(二)NSOperation VS GCD

    原创Blog.转载请注明出处 blog.csdn.net/hello_hwc 欢迎关注我的iOS SDK具体解释专栏 http://blog.csdn.net/column/details/huang ...

  7. iOS多线程开发之NSThread

    一.NSThread基本概念 NSThread是基于线程使用,轻量级的多线程编程方法(相对GCD和NSOperation),一个NSThread对象代表一个线程,需要手动管理线程的生命周期,处理线程同 ...

  8. iOS 多线程学习笔记 —— NSOperation

    本文复制.参考自文章:iOS多线程编程之NSOperation和NSOperationQueue的使用 ,主要为了加强个人对知识的理解和记忆,不做他用.原作者声明: 著作权声明:本文由http://b ...

  9. iOS游戏开发之UIDynamic

    iOS游戏开发之UIDynamic 简介 什么是UIDynamic UIDynamic是从iOS 7开始引入的一种新技术,隶属于UIKit框架 可以认为是一种物理引擎,能模拟和仿真现实生活中的物理现象 ...

随机推荐

  1. Asynchronous and Distributed Programming in R with the Future Package

    Every now and again someone comes along and writes an R package that I consider to be a 'game change ...

  2. Sampling Distributions and Central Limit Theorem in R(转)

    The Central Limit Theorem (CLT), and the concept of the sampling distribution, are critical for unde ...

  3. collections 模块:更多数据结构

  4. SQL注入攻击[详解]

    SQL注入攻击是黑客对数据库进行攻击的常用手段之一.随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员也越来越多.但是由于程序员的水平及经验也参差不齐,相当大一部分程序员在编写代码的时候, ...

  5. 跨域请求,jsonp

    其实跨域请求,只需要在请求的url后面加上callback=?即可. 提供以下两种获取跨域的ajax的写法,都是基于jQuery.都已经成功使用,兼容做到ie7,(ie6未测试);案例地址来自豆瓣开放 ...

  6. WPF中实现类智能感知

    首先要做的事情就是定义一个popup来显示我们需要展示的东西 <Popup x:Name=" StaysOpen="False" Placement="B ...

  7. springmvc 之 深入核心研究

    一.前言:二.核心类与接口:三.核心流程图四.DispatcherServlet说明五.双亲上下文的说明六.springMVC-mvc.xml 配置文件片段讲解 七.如何访问到静态的文件,如jpg,j ...

  8. 如何给两个swiper建立关系

    单个swiper已经满足不了需求了. 各种花式轮播已经慢慢进入市场.swiper该如何立足,那么请看. <div class="swiper-container"> & ...

  9. thinkphp中fetch渲染模板的处理

    <script type="text/javascript"> function xiugai(elm){ var formData1=$("#a1_&quo ...

  10. 【LeetCode】105 & 106. Construct Binary Tree from Inorder and Postorder Traversal

    题目: Given inorder and postorder traversal of a tree, construct the binary tree. Note:You may assume ...