iOS多线程之四:NSOperation的使用

一、NSOperation NSOperation是苹果公司对GCD的封装,完全面向对象,但是比GCD拥有更强的可控性和代码可读性。所以使用起来更好理解。 NSOperation是一个抽象类,它不能直接使用,所以你必须使用NSOperation子类,使用最多的是它的两个子类:NSInvocationOperation和NSBlockOperation,但你也可以继承NSOperation来创建自己的类来执行操作。

1、NSBlockOperation:使用这个类来用一个或多个block初始化操作,操作本身可以包含多个块。当所有block被执行操作将被视为完成。 2、NSInvocationOperation:使用这个类来初始化一个操作,它包括指定对象的调用selector。

Paste_Image.png

另外还有一些其他的方法: 取消操作:

[operation cancel];

返回当前操作相对于调用start方法的线程是同步还是异步执行,默认返回是NO,表示操作与调用线程同步执行。

[operation isConcurrent];

二、NSInvocationOperation 使用这个类来初始化一个操作,它包括指定对象的调用selector。

Paste_Image.png

ps: 使用NSInvocationOperation开启一个任务默认是在主线程中执行,只有添加到队列中才会开启新的线程。即如果操作没有放到队列中queue中,都是同步执行。只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作。

三、NSBlockOperation 使用这个类来用一个或多个block初始化操作,操作本身可以包含多个块。当所有block被执行操作将被视为完成。

Paste_Image.png

从运行的结果可以看出,NSBlockOperation确实实现了多线程。但是我们可以看到,它并非是将所有的block都放到放到了子线程中。通过上面的打印记录我们可以发现,它会优先将block放到主线程中执行,若主线程已有待执行的代码,就开辟新的线程,但最大并发数为4(包括主线程在内)。如果block数量大于了4,那么剩下的Block就会等待某个线程空闲下来之后被分配到该线程,且依然是优先分配到主线程。

四、自定义NSOperation 如果NSInvocationOperation和NSBlockOperation对象不能满足需求,可以自定义NSOperation,添加我们想要的功能。目前,我们可以自定义非并发和并发两种不同类型的NSOperation子类,而自定义一个前者要比后者简单得多。

1、非并发的operation 对于一个非并发的operation,我们需要做的就只是执行main方法中的任务以及能够正常响应取消事件就可以了,其它的复杂工作比如依赖配置、KVO 通知等NSOperation类都已经帮我们处理好了。

Paste_Image.png

Paste_Image.png

2、正确响应取消事件 当一个operation开始执行后,它会一直执行它的任务直到完成或被取消为止。可以在任意时间点取消一个operation,甚至在operation执行之前。尽管NSOperation提供了一个方法,让应用取消一个操作,但是识别出取消事件则是我们自己的事情。如果operation直接终止, 可能无法回收所有已分配的内存或资源。为了让自定义的operation能够支持取消事件,NSOperation对象需要定期地调用isCancelled方法检测操作是否已经被取消,如果返回YES(表示已取消),则立即退出执行。根据苹果官方的说法,isCancelled方法本身是足够轻量的,所以就算是频繁地调用它也不会给系统带来太大的负担。

以下地方可能需要调用isCancelled:

  • 在开始执行任务之前。
  • 在循环的每次迭代过程中,如果每个迭代相对较长可能需要调用多次。
  • 代码中相对比较容易中止操作的任何地方。

Paste_Image.png

ps:并发的operation,我会在有时间单独拿出来。

五、NSOPerationQueue NSOperationQueue就是执行NSOperation的队列,我们可以将一个或多个NSOperation对象放到队列中去执行,而且是异步执行的。一个NSOperation对象可以通过调用start方法来执行任务,但是默认是同步执行的。

Paste_Image.png

从运行结果可以看出,NSInvocationOperation和NSBlockOperation是异步执行的,NSBlockOperation中的每一个Block也是异步执行且都在子线程中执行,并且这些任务是并行执行的。

六、添加NSOperation的依赖对象 当某个NSOperation对象依赖于其它NSOperation对象的完成时,就可以通过addDependency方法添加一个或者多个依赖的对象,只有所有依赖的对象都已经完成操作,当前NSOperation对象才会开始执行操作。需要先添加依赖关系,再将操作添加到队列中。另外,通过removeDependency方法来删除依赖对象。

Paste_Image.png

从运行结果中可以看出,默认是按照添加顺序执行的,先执行operation1,再执行operation2

场景:设置线程1依赖线程2,线程2依赖线程3,线程3依赖线程4。

Paste_Image.png

从运行结果中看出,线程1依赖线程2,线程2依赖线程3,线程3依赖线程4。

ps: 使用依赖关系需要注意,依赖关系不局限于相同queue中的NSOperation对象,NSOperation对象会管理自己的依赖, 因此完全可以在不同的queue之间的NSOperation对象创建依赖关系。不能创建环形依赖,比如A依赖B,B依赖A,这是错误的。

七、设置queue的最大并发操作数量 由于并发的线程越多越耗资源,在queue队列中可以设置同时并发线程的数量,来进行控制,通过调用setMaxConcurrentOperationCount方法可以设置queue的最大并发操作数量。

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 10;

设置queue的最大并发操作数量需要注意 1、最大并发数是有上限的,即使你设置为100,它也不会超过其上限,而这个上限的数目也是由具体运行环境决定的。 2、设置最大并发数一定要在NSOperationQueue初始化后设置。

八、取消Operations 线程调用start方法后并不是立即执行,而是进入一个就绪的状态,由系统调度执行。一旦添加到operation queue,queue就拥有了这个Operation对象并且不能被删除,唯一能做的事情是取消。你可以调用Operation对象的cancel方法取消单个操作。

for (NSOperation *operation in self.queue.operations) {
    // 取消一个NSOperation
    [operation cancel];
}

也可以调用operation queue的cancelAllOperations方法取消当前queue中的所有操作。

// 取消queue中所有的操作  
[queue cancelAllOperations];  

九、暂停和继续queue 如果你想临时暂停Operations的执行,可以使用queue的setSuspended:方法暂停queue。不过暂停一个queue不会导致正在执行的operation在任务中途暂停,只是简单地阻止调度新Operation执行。你可以在响应用户请求时,暂停一个queue来暂停等待中的任务。稍后根据用户的请求,可以再次调用setSuspended:方法继续queue中operation的执行。

// 暂停queue  
[queue setSuspended:YES];  

// 继续queue  
[queue setSuspended:NO];  

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏蓝天

Linux下共享库(SO)有关的几个环境变量

Linux支持共享库已经有悠久的历史了,不再是什么新概念了。大家都知道如何编译、连接以及动态加载(dlopen/dlsym/dlclose) 共享库。但是,...

17110
来自专栏和蔼的张星的图像处理专栏

1.Win10+VsCode的C/CPP编译环境搭建

我是从开始学C++的时候就一直用的是visual studio,毕竟宇宙第一IDE,写和调试都是超级方便快捷,唯一的缺点可能就是启动慢一点。 之前电脑没有换固...

86060
来自专栏Golang语言社区

goroutine背后的系统知识

Go语言从诞生到普及已经三年了,先行者大都是Web开发的背景,也有了一些普及型的书籍,可系统开发背景的人在学习这些书籍的时候,总有语焉不详的感觉,网上也有若干流...

35340
来自专栏Java帮帮-微信公众号-技术文章全总结

Python常见面试题【悟空教程】

1.MySQL 数据库总结 MySQL 可以建多少个数据库,理论上是没有限制的,每一个数据库可以有上亿的对象,但是一般基于硬件要求、效率问题一般不超过64个, ...

20320
来自专栏技术之路

go微服务框架go-micro深度学习(二) 入门例子

    上一篇帖子简单介绍了go-micro的整体框架结构,这一篇主要写go-micro使用方式的例子,中间会穿插一些go-micro的源码,和调用流程图,帮大...

1.9K50
来自专栏Golang语言社区

goroutine背后的系统知识

Go语言从诞生到普及已经三年了,先行者大都是Web开发的背景,也有了一些普及型的书籍,可系统开发背景的人在学习这些书籍的时候,总有语焉不详的感觉,网上也有若干流...

29250
来自专栏Golang语言社区

goroutine背后的系统知识

Go语言从诞生到普及已经三年了,先行者大都是Web开发的背景,也有了一些普及型的书籍,可系统开发背景的人在学习这些书籍的时候,总有语焉不详的感觉,网上也有若干...

35480
来自专栏JadePeng的技术博客

axios介绍与使用说明 axios中文文档

本周在做一个使用vuejs的前端项目,访问后端服务使用axios库,这里对照官方文档,简单记录下,也方便大家参考。 Axios 是一个基于 Promise 的 ...

2.8K90
来自专栏后台全栈之路

基于汇编的 C/C++ 协程 - 实现

将 libco 和 libevent 两者的功能糅合起来,所以我把我的工程,命名为 libcoevent,意为 “基于 libevent 的同步协程服务器编程框...

60430
来自专栏丑胖侠

Zookeeper开源客户端Curator之Master/Leader选举

在实际生产中,特别是分布式系统中,我们经常遇到这样的场景:一个复杂的任务,近需要从分布式机器中选出一台机器来执行。诸如此类的问题,我们统称为“Master选举”...

439100

扫码关注云+社区

领取腾讯云代金券