创建一个支持异步操作的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(举个例子),我们总是用这些数字进行网页服务器的 ...
一周排行
  • 概述Application对象是Microsoft Office Excel 2007对象模型中最高级别的对象,表示Excel程序自身.Application对象提供正在运行的程序的信息.应用于程序实例的选项以及实例 ...
  • Packet Tracer 5.2 之 帧中继实验 实验分两部分:一.在Packet Tracer上边画好拓扑,并配置好模块和帧中继DLCI 先看下配好的拓扑图: 配置过程:1.添加3台路由器,我用的是2811,为路 ...
  • 使用微软IEAK9
    微软IEAK 9(Internet Explorer Administration Kit ...
  • 常用的负载均衡开源软件有: nginx.lvs.keepalived 商业的硬件负载设备: F5.Netscale1. LB.LVS介绍LB集群是load balance 集群的简写,翻译成中文就是负载均衡集群LVS ...
  • 最近老师布置Linux作业,大家都开始在VMware种安装Linux,但很多人遇到一个问题,虚拟机不能上网.    VMware默认网络是桥接模式(相当于真机和虚拟机是接在同一台交换机上),并且是自动获得地址,一般寝 ...
  • WindowsAzure下Apache服务可用性集配置介绍
    Windows Azure下Apache服务可用性集配置介绍Azure现在不是一个什么新鲜 ...
  • 1.substring(start,end) 不包括end本身:如果start>end,两者的位置互换:如果start(或end)小于0,则变为0var str = "Hello world!&quo ...
  • 17款jQuery在线QQ客服代码分享给大家咯!!拿走,不谢,我叫雷锋~~jQuery侧边栏点击展开收缩在线QQ客服代码jQuery网页右侧固定层显示隐藏在线qq客服代码jQuery点击按钮遮罩弹出在线QQ客服代码j ...
  • 1.147,258规则:下家丢1万,3.4.7万基本不吃,2.5万可能要吃:2.牌过半旬,上家开始落风子,不要碰(碰听张除外):3.牌局一直不胡,最好不要动牌,要打熟张,牌一动就有吃大牌的可能:4.下家丢3.8万,有 ...
  • 一般在一个框架库里面,我们继承一个库里已有的类,但是又不想把它周边的一套完全重整,所以希望我们定义的这个类能像 类库里那个类一样到处能用,这时候只要我们保证把类库里原有的public接口全部完整实现(或者直接用基类的 ...