iOS多线程应用(三):NSOperation

写在前面

本系列文章列表

NSOperation是基于GCD的面向对象封装,在各大开源库里面我们常常看到它的身影。它的使用很简单易懂,基本上你点进Api就会用了,本文就最常用的功能进行讲解,更多的多线程原理还是看本系列文章的GCD部分。

一、NSBlockOperation和NSInvocationOperation

这两个类都是NSOperation的子类,它们的区别可能就是一个是用block回调,一个是用NSInvocation 回调,我们通常直接使用的也是这两个类,下面代码示意如何创建任务:

NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{}];

NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(respondsToOperation:) object:nil];

都不用多说了,这就是两个任务,非常简单,当然,它还有一系列的方法表示状态:executing、finished、cancelled,以及开启和取消:start、cancel,和NSThread一样有main方法可重写。

二、NSOperationQueue 队列

和GCD一样,NSOperation保留了队列的概念,但是没有具体的并行串行的概念了,但是我们可以实现它。

先来看这样一段代码:

NSLog(@"主线程开始");

NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任务0 %@", [NSThread currentThread]);
}];
for (int i = 1; i < 5; i++) {
    [blockOperation addExecutionBlock:^{
        NSLog(@"任务%d: %@", i, [NSThread currentThread]);
        [NSThread sleepForTimeInterval:3];
    }];
}
[blockOperation start];

NSLog(@"主线程结束");
主线程开始
任务3: <NSThread: 0x600000276080>{number = 5, name = (null)}
任务2: <NSThread: 0x604000465740>{number = 4, name = (null)}
任务1: <NSThread: 0x604000465700>{number = 3, name = (null)}
任务0 <NSThread: 0x600000077300>{number = 1, name = main}
任务4: <NSThread: 0x600000077300>{number = 1, name = main}
主线程结束

上机打印出来的效果来看,这些任务是并行执行的,但是会阻塞主线程直到任务全部都执行完成。显然这不能满足我们的全部需求,所以我们把任务加入队列试试(加入队列任务自动执行):

NSLog(@"主线程开始");

NSOperationQueue *queue = [NSOperationQueue new];

NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任务0 %@", [NSThread currentThread]);
}];

for (int i = 1; i < 5; i ++) {
    [blockOperation addExecutionBlock:^{
        NSLog(@"任务%d %@", i, [NSThread currentThread]);
    }];
}

[queue addOperation:blockOperation];

//[blockOperation waitUntilFinished];

NSLog(@"主线程结束");
主线程开始
主线程结束
任务0 <NSThread: 0x600000475c40>{number = 3, name = (null)}
任务1 <NSThread: 0x600000475f80>{number = 5, name = (null)}
任务4 <NSThread: 0x600000475ec0>{number = 4, name = (null)}
任务3 <NSThread: 0x600000475c40>{number = 3, name = (null)}
任务2 <NSThread: 0x600000475f80>{number = 5, name = (null)}

从打印结果来看,这是并行队列+异步任务,这基本满足我们后台执行耗时任务的需求了。

你们应该也注意到了我注释了一句代码[blockOperation waitUntilFinished];,现在将它打开,运行得到的结果和方法的名字一样,它会阻塞当前线程直到任务全部结束,当然NSOperationQueue也有这么一个方法:waitUntilAllOperationsAreFinished

maxConcurrentOperationCount

最大并发数,通过设置这个我们可以有效的控制并发任务的数量,从而对性能进行有效的控制。

三、addDependency 添加任务依赖

我们给NSOperation添加依赖的目的,主要是实现串行队列的效果。

NSLog(@"主线程开始");

NSOperationQueue *queue = [NSOperationQueue new];

NSBlockOperation *operation0 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任务1: %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任务2: %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任务3: %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任务4: %@", [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1.0];
}];

[operation1 addDependency:operation0];
[operation2 addDependency:operation1];
[operation3 addDependency:operation2];
//不要添加相互依赖。理论上不管任务在任何队列都可以添加依赖,不过不建议这么做。

[queue addOperations:@[operation0, operation1, operation2, operation3] waitUntilFinished:YES];

NSLog(@"主线程结束");
主线程开始
任务1: <NSThread: 0x604000474780>{number = 3, name = (null)}
任务2: <NSThread: 0x6040004747c0>{number = 4, name = (null)}
任务3: <NSThread: 0x604000474780>{number = 3, name = (null)}
任务4: <NSThread: 0x6040004747c0>{number = 4, name = (null)}
主线程结束

实验证明,达到了我们预期的效果,任务1到任务4都是串行执行的,并且addOperations: waitUntilFinished:方法若第二个参数为YES,会阻塞主线程。

写在后面

NSOperation的用法不见得有GCD简洁,但它符合面向对象的编程思想,而且便于在我们的业务代码中穿梭,所以它的使用场景还是比较多的,没什么难点可言,本文列举的用法已经可言解决大多数的场景了,如果想多了解,直接看一遍苹果官网的API就OK了?。

本系列文章列表

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

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏转载gongluck的CSDN博客

第14章 高级I/O函数

设置套接字时间限制: 1、使用alarm函数和SIGALRM信号 2、使用由select提供的时间限制 3、使用SO_RCVTIMEO和SO_SNDTIM...

30040
来自专栏美团技术团队

ReactiveCocoa中潜在的内存泄漏及解决方案

ReactiveCocoa是GitHub开源的一个函数响应式编程框架,目前在美团App中大量使用。用过它的人都知道很好用,也确实为我们的生活带来了很多便利,特别...

54280
来自专栏向治洪

RCTEventEmitter使用

在0.27版本之前,RN的Native端向js端发射消息主要通过sendDeviceEventWithName的方式,相关代码如下。 @synthesize b...

47970
来自专栏一“技”之长

iOS通过NSUserDefaults实现简单的应用间数据传递

NSUserDefaults是用于保存应用程序设置,应用信息等轻量级数据的的一个类,其本质是将数据写为plist文件的形式保存在本地。在IOS中,系统为每一个应...

9920
来自专栏技术之路

ios 接收 c# socket udp 组播

最近用wcf 服务 给ios和安卓做接口,做了几个ios的项目  用udp 组播 让ios多终端接收和刷新方法 做一个简单的小例子会把工程给大家下载的   c#...

27580
来自专栏慎独

UIViewController生命周期分析

18040
来自专栏Alice

iOS 获取通讯录里边的电话号码AddressBook

1  首先导入库 <AddressBook/AddressBook.h> 2 然后在导入#import <AddressBook/AddressBook.h>文...

307100
来自专栏陈满iOS

[iOS源码笔记]·第三方网络图片处理框架:SDWebImage网络下载及缓存管理策略

typedef void(^SDExternalCompletionBlock)(UIImage * _Nullable image, NSError * _N...

15410
来自专栏iOS122-移动混合开发研究院

ReactiveCocoa,最受欢迎的iOS函数响应式编程库(2.5版),没有之一!

简介 项目主页: ReactiveCocoa 实例下载: https://github.com/ios122/ios122 简评: 最受欢迎,最有价值的iOS响...

27980
来自专栏遊俠扎彪

CentOS 5.6 下使用 vsFTPd 架设 FTP Server

主要配置文件/etc/vsftpd/vsftpd.conf,配置如下:

24950

扫码关注云+社区

领取腾讯云代金券