创建一个支持异步操作的operation

NSOperationQueue时iOS中常用的任务调度机制。在创建一个复杂任务的时候,我们通常都需要编写?NSOperation的子类。在大部分情况下,重写main方法就可以满足要求。main方法执行完毕后,系统就会认为这个operation完成了。

有时候情况并没有这么简单。我们需要在operation中调用异步的API,这个API会通过一个block或者代理通知我们结果。这时只靠覆盖main方法就显得力不从心了。因为异步API尚未执行完毕,operation就提前结束了。

怎么解决这个问题呢?我想到AFNetworking中有同样的案例,于是参考了其中的实现,设计了一个基于异步任务的operation。

我们需要覆盖start方法。这个方法的作用有点类似于main方法,在这里实现任务的代码逻辑。系统怎么知道我们的任务开始执行,或者完成了呢?系统会通过KVO的形式,监听operation的一些属性。我们可以重新实现这些属性,来通知系统任务执行的状态。重要的属性有:

@property (readonly, getter=isReady) BOOL ready;
@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;

它们都是只读属性。我们可以简单的重写它们,返回我们想要的值。但是如何通知系统它们的值已经变化了呢?如果你了解KVO,那么你就应该知道NSObject的这一对方法能够帮助我们,可以利用它们手动通知系统某个属性发生了变化。

- (void)willChangeValueForKey:(NSString *)key;
- (void)didChangeValueForKey:(NSString *)key;

下面就是完整的代码。这里只用了一个NSTimer模拟一个异步的任务。在state变化时,我们需要通知KVO系统operation的状态发生了变化。这一步很重要,我刚开始忽略了手动通知KVO,导致任务永远无法完成(即使start中的任务全部执行完毕)。

typedef NS_ENUM(NSInteger, MyOperationState) {
    MyOperationStateReady,
    MyOperationStateExecuting,
    MyOperationStateFinished
};

@interface MyOperation : NSOperation

@property (nonatomic, strong) NSTimer *exeTimer;
@property (nonatomic, assign) MyOperationState state;
@property (nonatomic, strong) NSLock *lock;

@end

@implementation MyOperation

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.lock = [NSLock new];
        [self willChangeValueForKey:@"isReady"];
        self.state = MyOperationStateReady;
        [self willChangeValueForKey:@"isReady"];
    }
    return self;
}

- (void)start
{
    [self.lock lock];
    if (!self.finished && self.state == MyOperationStateReady) {
        [self willChangeValueForKey:@"isExecuting"];
        self.state = MyOperationStateExecuting;
        [self didChangeValueForKey:@"isExecuting"];

        self.exeTimer = [NSTimer timerWithTimeInterval:5 target:self selector:@selector(finish) userInfo:nil repeats:NO];
        [[NSRunLoop mainRunLoop] addTimer:self.exeTimer forMode:NSRunLoopCommonModes];
    }
    [self.lock unlock];
}

- (void)cancel
{
    [self.lock lock];
    if (!self.isFinished && !self.cancelled) {
        [super cancel];
        [self.exeTimer invalidate];
    }
    [self.lock unlock];
}

- (BOOL)isReady
{
    return self.state == MyOperationStateReady;
}

- (BOOL)isExecuting
{
    return self.state == MyOperationStateExecuting;
}

- (BOOL)isFinished
{
    return self.state == MyOperationStateFinished;
}

- (BOOL)isAsynchronous
{
    return YES;
}

- (BOOL)isConcurrent
{
    return YES;
}

- (void)finish
{
    [self.lock lock];
    [self willChangeValueForKey:@"isFinished"];
    self.state = MyOperationStateFinished;
    [self didChangeValueForKey:@"isFinished"];
    [self.lock unlock];
}

@end
更多相关文章
  • 在一个空ASP.NETWeb项目上创建一个ASP.NETWebAPI2.0应用
    由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET MVC的书籍“额外奉送”的),以至于很多人会觉得ASP.NET Web API仅仅是ASP.NET M ...
  • 使用WindowsVirtualPC创建一个虚拟机
    使用 Windows Virtual PC 创建一个虚拟机         之前我们对 Windows Virtual PC & Windows XP Mode 有所了解!除了可以用于解决企业中所遇到的应用程序兼容性问题以外,其实通过 Windows Virtual PC 也能创建其他虚拟机 ...
  • SCVMM2012R2服务模板系列五创建一个开箱即用的双层服务模版
    有关开箱即用的单层Web应用服务模版,可参考之前的文章:http://maomaostyle.blog.51cto.com/2220531/1336130这次分享的是追加了SQL层的双层模板,使得整套服务更加的饱满:前端应用不必指向已有的SQL实例,这样不同用户的数据源相对独立,可以更自由的管理和维 ...
  • 概述Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支持框架语言Visual Basic, Visual C#, IronRuby, Ironpython,对JSON.Web Service.WCF以及Sockets的支持等一系列新的 ...
  • AWS-如何创建一个个人博客
    昨天豆子第一次接触了aws,并且成功创建并且访问了一个window 的虚拟机.今天豆子尝试创建了Linux实例, 成功的安装了LAMP,用wordpress发布了一个个人博客,并且连接了自己的域名,整个过程大概花了2个小时,个人感觉对于一个新手来说,aws上手真的很容易.有兴趣的可以看看豆子创建的b ...
  • 如何轻松创建一个Windows8可启动的USB闪盘(WindowstoGo)
    杂序[今天早上又是一个泥泞而拥堵的北京,对于北京"首堵"的交通吐槽是没有什么用用处的,最好的方式还是找到一个消遣的方式,而对于我正好利用和打不车奔波在北京各地铁线路的"胖子"聊聊电话粥,呵呵.] 动因到了公司,恰巧一个同事和我说他做的Windows to Go ...
  • 转怎样创建一个Xcode插件Part1
    原文:How To Create an Xcode Plugin: Part 1/3 原作者:Derek Selander 译者:@yohunl 译者注:原文使用的是xcode6.3.2,我翻译的时候,使用的是xcode7.2.1,经过验证,文章中说说的依然是有效的.在文中你可以学习到一系列的技能, ...
  • 1.什么是DNS?(Domain Name System)域名系统.DNS其实实现的功能很简单也很有效,它能够让用户可以不用记得那些经常要访问服务器的ip地址,直接要你输入类似拼音格式的就可以访问到那些数字串的ip地址.假设以61.120.155.14(举个例子),我们总是用这些数字进行网页服务器的 ...
一周排行
  • 有时候php业务的网站突然无法访问,我们会去查找原因,首先会判断是不是web服务器挂了,然后再去查询是不是数据库挂了,这里提供一个脚本自动去检测是web服务器和数据库服务器,去确定到底是哪个服务器挂掉,并自动去启动相 ...
  • 前序:      3天前,有幸得到师兄赏识,和他一起去帮一间珠海的本地的IT公司担任面试官,虽说如此,我自己本身就还没毕业,充其量是去见识下世面罢了.当天共面试了13人,这只是上午,下午我闪了.在笔试的部分,我设置了 ...
  • 在SecureFx中看到的文件名不是乱码,通过SecureCRT远程登录查看到的文件名是乱码.而通过SecureCRT远程登录后直接创建的中文文件名在SecureCRT中正常,但是在SecrueFX看到的这个文件名又 ...
  •  一.实验环境http://prdownloads.sourceforge.net/sourceforge/nagiosplug/nagios-plugins-1.4.14.tar.gzwget http://prd ...
  • grant select any dictionary to scott;create table t1 as select * from emp;insert into t1 select * from t1;-- ...
  • Office2010最佳体验之同工作齐分享
    ----=即刻火速下载Office 2010,感受非凡体验!=---- 本文讲述Offic ...
  • js内存泄漏
    IE和webkit浏览器都是采用计数来处理垃圾,也就是说每个对象被引用一次,该对象的计数器 ...
  • 争取一天看一个文件,随便找一个好看的看起eva/src/api/evaipseek#ifndef EVAIPSEEKER_H #define EVAIPSEEKER_H #include <string> ...
  •   MySQL的mysqldump将表中的数据导出成insert语句,导入数据时执行的是insert操作,如果原表里面是保存着BLOB数据,在导入时会发生错误.  在导出时加上--hex-blob参数,即可防止BLO ...
  • 本文主要从硬件搭建.软件设计两方面描述基于IPC阵列的3D信息获取方式.