专栏首页iOS 开发杂谈iOS多线程之四:NSOperation的使用

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 条评论
登录 后参与评论

相关文章

  • iOS多线程之一:基本概念

    进程:就是一个正在执行的程序。 线程:是执行程序最基本的单元,它有自己栈和寄存器。

    s_在路上
  • iOS多线程之三:GCD的使用

    一、什么是GCD GCD是Grand Central Dispatch的简称,它是基于C语言的。如果使用GCD,完全由系统管理线程,不需要编写线程代码。只需定...

    s_在路上
  • iOS多线程之二:NSThread的使用

    NSThread NSThread封装性最差,最偏向于底层,主要基于thread使用,生命周期需要手动管理,所以这套方案也是偶尔用用,比如 [NSThread...

    s_在路上
  • Jmeter系列(8)- test plam测试计划参数详解

    https://www.cnblogs.com/poloyy/category/1746599.html

    小菠萝测试笔记
  • 我对JS延迟异步脚本的思考

    我一共写了2个async和2个defer标签,其它的都是普通标签.其中async1.js里面有4000行代码,其它都是一个console.log而已

    Peter谭金杰
  • kubernetes集群搭建(3):master节点安装

    1.  192.168.100.6:5000/rhel7/pod-infrastructure:1.0 为私库中的地址,默认配置文件中地址被和谐了,所以可以获取...

    肖哥哥
  • org.springframework.util.StopWatch:简洁的耗时统计小工具

    想知道一个代码块执行耗时多久,通常做法是执行前记录当前时间A,执行后用当前时间减去A就是耗时了。spring库中有个统计耗时的小工具:StopWatch类,它可...

    程序员欣宸
  • 什么是物联网平台?

    无论您是IoT还是经验丰富的老将,您可能以前听说过“IoT Platform”一词。毕竟,去年有超过300个物联网平台,这个数字继续快速增长(我听说现在有700...

    首席架构师智库
  • 从0学习MySQL系列(二)安装篇

    安装包 ---- https://dev.mysql.com/downloads/ 概述 ---- 本文讲解Windows, Ubuntu, ...

    赵腰静
  • 爬虫+网站开发实例:电影票比价网

    时常有同学会问我类似的问题:我已经学完了 Python 基础,也照着例子写过一点爬虫代码 / 了解过 django 的入门项目 / 看过数据分析的教程……然后就...

    Crossin先生

扫码关注云+社区

领取腾讯云代金券