iOS多线程之NSOpearation

在 iOS 开发中,异步操作通常使用 GCD 的方式来完成,GCD 可以简单快速完成异步操作,当如果涉及到高级的操作的时候如异步依赖、暂停、结束、优先级往往会要写很多代码,而 GCD C语言式的写法,看起来也不是那么的“优雅”,NSOpearation 是基于 GCD 的封装,提供面向对象的形式,容易将异步任务封装成一个个异步“操作”对象,也会让代码组织更加优雅,许多第三方库都在使用 NSOpearation 来完成异步任务,如 SDWebimage,NSOpearation 在 GCD 之外提供我们另外一个选择。

NSOpearation

NSOpearation表示了一个独立的异步操作单位,它是一个抽象类,我们不能直接使用它,我们需要使用它的子类

  • NSInvocationOperation
  • NSBlockOperation
  • 自定义的 NSOpearation

其中 NSInvocationOperation 和 NSBlockOperation 是系统提供,如果不满足我们可以使用自定义。自定义我们在后文讲。

NSInvocationOperation 与 NSBlockOperation

创建 Opearation

NSInvocationOperation是通过一个 object 和 selector 来生成一个 Operation, 如下代码

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

NSBlockOperation可以通过一个或者多个 block 来生成 Operation

    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block2");
    }];
    
    [blockOperation addExecutionBlock:^{
        NSLog(@"block2");
    }];
    
    [blockOperation start];

执行Opearation

NSOpearation 及其子类有 2 种执行方式

  1. 调用 start
  2. 添加到 NSOperationQueue

start 方法:start 方法一般会在调用 start 方法的线程执行,会阻塞当前线程,但 NSBlockOperation 中如果添加了多个执行的 block 时,不一定会在调用的线程中执行

添加到 NSOperationQueue:当一个 NSOpearation 被添加到 NSOperationQueue 中的时候,NSOperationQueue 会为 NSOpearation 提供线程,这个时候始终是在异步线程的中执行。

同步的 Opearation 和异步 Opearation

当我们使用 start 执行一个 Opearation 时,这个 Opearation 将会在调用 start 方法的线程执行,这是同步的。会阻塞的线程,这叫做同步的 Opearation,当然也可以在 start 方法中配置的执行的线程环境,使达到在异步执行的效果,或者将 Opearation 加入 NSOperationQueue 中执行,那么 NSOperationQueue 会自动给你切换到异步线程,这就叫异步 Opearation。

2 种形式的代码如下:

    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block");
    }];
//    [blockOperation start];
    NSOperationQueue *queue = [NSOperationQueue new];
    [queue addOperation:blockOperation];

自定义Operation

如果 NSInvocationOperation 与 NSBlockOperation 不能很好完成我们的需求,我们可以选择自定义 Operation,继承 NSOperation 即可实现自己的 Operation,当然也需要多花点代码来维护自己的 Operation

需要维护状态

作为自定义的 Operation 我们需要一些状态来维护外界对我们的观察。

asynchronous(非必要)

这个属性表示当前 Operation 时候需要异步运行,默认是 NO。这个属性对 Operation 执行线程没有关系,即使返回 YES,如果是直接手动调用 start 方法,那么这个 Operation 也是运行在调用线程中。

cancelled

当 Operation 的 cancel 方法被调用的时候, cancelled 会被设置为 YES,Operation 执行一个长时间的异步任务的时候,需要经常检查这个 Operation 是否被取消,也就是 cancelled 是否为 YES,如果检测到 Operation 被取消,我们需要及时响应取消任务事件。

executing (必须维护)

表明当前的 Operation 正在执行中,需要自己维护

finished (必须维护)

表明当前的 Operation 正在已经结束,需要自己维护,如果对 Operation 添加依赖,那么这个属性很重要。因为依赖的执行取决于被依赖的那个 Operation 的 finished 是不是 YES, 否则不会开始依赖任务。

需要重写的方法

start

如果你的 Operation 不是异步执行的,那么这个方法你可以不用重写,如果你的 Operation 是一个异步执行的任务,那么需要重写 start 方法(不用调 super 的 start)来为 Operation 配置线程和一些初始化操作,当然你的 Operation 是加到 NSOperationQueue 中的,那么你也可以选择不重写这个 start 方法。

main

重写 main 方法(不用调用super 的 main)是可选择的,尽管在 start 方法我们可以写执行任务的代码,但是我们可以把执行任务的代码放到 main 中达到配置和执行分离的目的,使得代码结构变得清晰,这里有个地方要注意,如果你重写了 start 方法,那么记得调用下 main 方法,因为默认的 start 方法是会调用的,重写了需要我们手动加上。

维护 KVO 通知

在我们重写了大量方法后,我们需要自己维护 KVO 通知,如以下几个通知我们应该按需来维护

isCancelled
isConcurrent
isExecuting
isFinished
isReady
dependencies
queuePriority
completionBlock

Operation 的依赖

我们可以使用

- (void)addDependency:(NSOperation *)op;
- (void)removeDependency:(NSOperation *)op;

这 2 个方法来为 Operation 添加依赖,添加依赖之后,被依赖的那个 Operation 结束后,并且 isFinshed 状态为 YES ,这个时候依赖的 Operation 才会运行,添加依赖关系后,即使加入不同的 OperationQueue 它们的依赖关系一样存在。

Operation 的优先级

除了设置依赖,我们还可以设置 Operation 的优先级,优先级高的会被优先级低的优先执行。系统提供了 5 个等级

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

优先级生效的必要条件是 Operation 的 isReady 为 YES 状态
比如,在一个 operation queue 中,有一个高优先级和一个低优先级的 operation ,并且它们的 isReady 状态都为 YES ,那么高优先级的 operation 将会优先执行。而如果这个高优先级的 operation 的 isReady 状态为 NO ,而低优先级的 operation 的 isReady 状态为 YES 的话,那么这个低优先级的 operation 反而会优先执行。

Completion Block

在 Operation 结束后,我们可以为它设置一个 Completion Block 来获取完成之后的操作。我们没有办法保证 completion block 被回调时一定是在主线程,理论上它应该是与触发 isFinished 的 KVO 通知所在的线程一致的。

Operation Queue

Operation Queue 是 Operation 的载体,可以为 Operation 提供线程,提供依赖关系,提供优先级机制和线程调度。我们可以把 Operation 加入到 Queue 中运行。

添加 Operation

我们可以使用下面 3 个 API 来把 Operation 添加到 Queue,

addOperation: ,添加一个 operation 到 operation queue 中;
addOperations:waitUntilFinished: ,添加一组 operation 到 operation queue 中;
addOperationWithBlock: ,直接添加一个 block 到 operation queue 中,而不用创建一个 NSBlockOperation 对象。

控制最大的并发数量

设置 setMaxConcurrentoperationCount 可以控制并发数量,setMaxConcurrentoperationCount 为 1 也可以当做是一个串行队列。

暂停和恢复

通过设置 suspended 属性,可以挂起,和继续队列,也就是暂停和恢复,暂停执行 operation queue 并不能使正在执行的 operation 暂停执行,而只是简单地暂停调度新的 operation 。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容