一、什么是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. 虚拟机kali找不到无线网卡、搜不到无线网络

    VitualBox虚拟机下刚装好kali系统后,使用无线网卡,在主机上插一块usb无线网卡,然后进入虚拟机系统会发现无线网卡刚开始还能扫描出周围的无线网路, 过一会就搜不到了,显示无网络,输入命令iw ...

  2. cas4.2以下取消https

    deployerConfigContext.xml增加参数p:requireSecure="false" <bean class="org.jasig.cas.au ...

  3. css实现梯形(各种形状)的网页布局——transform的妙用

    在各式各样的网页中,经常会看到形状特别的布局,比如说下面的这种排版方式: 这种视觉上的效果,体验十分好.那么他是如何来实现的呢,博主在这里整理了如下2种实现的方式. 1.通过给 div 加border ...

  4. 用border或者div制作三角形等图形

    一般情况下, 我们设置盒子的宽高度, 及上下左右边框, 具体代码如下: 通过上述代码,div的具体样式如下: 现在在上面基础上, 我们把div的宽高度都设为0时, 现在我们再次查看效果,如下图: 这时 ...

  5. javascript基础-事件1

    原理 事件分两种.第一种浏览器事件,由浏览器抛出事件,它是人机交互的基础:第二种自定义事件,由程序员抛出事件,它是模拟事件流程.两者都是为了完成数据的传递. 浏览器事件 机制 冒泡和捕获两种机制.因I ...

  6. 教你怎么把iconfont转换成png透明图片

    1.进入iconfont图标库,登陆.http://www.iconfont.cn/ 2.选择想要的图标加入购物车. 3.直接选中下载图标为png格式.

  7. java基础之数组常用操作

    常用的对数组进行的操作 1.求数组中最大值,最小值 思路:假设下标为0的元素是最大值,遍历数组,依次跟max进行比较,如果有元素比这个max还大,则把这个值赋给max.最小值同样 public cla ...

  8. 从零开始理解JAVA事件处理机制(2)

    第一节中的示例过于简单<从零开始理解JAVA事件处理机制(1)>,简单到让大家觉得这样的代码简直毫无用处.但是没办法,我们要继续写这毫无用处的代码,然后引出下一阶段真正有益的代码. 一:事 ...

  9. maven无法加载本地jar包以及maven项目打包后本地jar包没有打进项目的问题解决办法

    1.首先设置依赖项,这样maven就会把该路径下的jar包导入项目引用 <dependency> <groupId>DPSDK-Manager</groupId> ...

  10. 利用angular给节点添加样式

    <!DOCTYPE html> <html lang="en" ng-app="myapp"> <head> <met ...