创建一个支持异步操作的operation
NSOperationQueue时iOS中常用的任务调度机制。在创建一个复杂任务的时候,我们通常都需要编写NSOperation的子类。在大部分情况下,重写main方法就可以满足要求。main方法执行完毕后,系统就会认为这个operation完成了。
有时候情况并没有这么简单。我们需要在operation中调用异步的API,这个API会通过一个block或者代理通知我们结果。这时只靠覆盖main方法就显得力不从心了。因为异步API尚未执行完毕,main方法并不会等待任务执行完毕,而是立即返回,系统就认为operation已经完成了。
怎么解决这个问题呢?我想到AFNetworking中有同样的案例,于是参考了其中的实现,设计了一个基于异步任务的operation。
我们需要覆盖start方法。这个方法的作用有点类似于main方法,在这里完成具体的任务。那么系统怎么知道我们的任务开始执行,或者完成了呢?系统会通过KVO的形式,监听operation的一些属性。我们可以重新实现这些属性,这样系统就可以监听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; // 用来记录operation的状态
@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) {
// 触发isExecuting属性的KVO观察者,这样系统就知道这个operation已经开始执行了
[self willChangeValueForKey:@"isExecuting"]; self.state = MyOperationStateExecuting; [self didChangeValueForKey:@"isExecuting"];
// 这里用一个timer模拟一个耗时的任务
self.exeTimer = [NSTimer timerWithTimeInterval: 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]; // 触发isFinished属性的KVO观察者,这样系统就知道这个operation已经执行完毕
[self willChangeValueForKey:@"isFinished"];
self.state = MyOperationStateFinished;
[self didChangeValueForKey:@"isFinished"];
[self.lock unlock];
}
@end
创建一个支持异步操作的operation的更多相关文章
- 最简单的dockerfile使用教程 - 创建一个支持SSL的Nginx镜像
什么是dockerfile?简单的说就是一个文本格式的脚本文件,其内包含了一条条的指令(Instruction),每一条指令负责描述镜像的当前层(Layer)如何构建. 下面通过一个具体的例子来学习d ...
- 创建一个支持ES6的Nodejs项目
文章来自于:https://www.codementor.io/iykyvic/writing-your-nodejs-apps-using-es6-6dh0edw2o 第一步:创建项目文件夹并初始化 ...
- 转:maven2创建一个eclipse工程,设置M2_REPO
from: http://tonychanhoho.iteye.com/blog/1584324 M2_REPO是一个用来定义 maven 2仓库在硬盘中的存储位置,windows默认是C:\User ...
- 30分钟用Restful ABAP Programming模型开发一个支持增删改查的Fiori应用
2016年时,Jerry曾经写过一系列关于SAP Fiori Smart Template(现在更名为Fiori Elements了)的博客,介绍了所谓的MDD开发方法论 - Metadata Dri ...
- 使用PHP创建一个REST API(Create a REST API with PHP)
译者前言: 首先这是一篇国外的英文文章,非常系统.详尽的介绍了如何使用PHP创建REST API,国内这方面的资料非常非常的有限,而且基本没有可操作性.这篇文章写的非常好,只要对PHP稍有了解的程序员 ...
- 简单创建一个SpringCloud2021.0.3项目(二)
目录 1. 项目说明 1. 版本 2. 用到组件 3. 功能 2. 上一篇教程 3. 创建公共模块Common 4. 网关Gateway 1. 创建Security 2. Security登陆配置 3 ...
- 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用
由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...
- 搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 (1)
搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 原文地址(英文):http://www.networkcomms.net/creating ...
- 5分钟创建一个SpringBoot + Themeleaf的HelloWord应用
第一步:用IDE创建一个普通maven工程,我用的eclipse. 第二步:修改pom.xml,加入支持SpringBoot和Themeleaf的依赖,文件内容如下: <?xml version ...
随机推荐
- iOS 的 APP 如何适应 iPhone 5s/6/6Plus 三种屏幕的尺寸?(转)
原文:http://www.niaogebiji.com/article-4379-1.html?utm_source=tuicool 初代iPhone 2007年,初代iPhone发布,屏幕的宽高是 ...
- 【jQueryMobile】Helloworld而页面切换
jQuery Mobile它是jQuery 在手机和平板设备的版本号. jQuery Mobile 它不仅会带来重大的移动平台jQuery核心库,而且会发布一个完整统一jQuery搬家UI相框.全球主 ...
- JSON数据格式介绍
JSON定义 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,易于阅读和编写,同一时候也易于机器解析和生成.它基于ECMA262语言规范(1999 ...
- ASP.NET仿新浪微博下拉加载更多数据瀑布流效果
闲来无事,琢磨着写点东西.貌似页面下拉加载数据,瀑布流的效果很火,各个网站都能见到各式各样的展示效果,原理大同小异.于是乎,决定自己写一写这个效果,希望能给比我还菜的菜鸟们一点参考价值. 在开始之前, ...
- Mantis Administrator控制密码、注册不用邮件验证、添加测试员[Z]
Mantis默认安装完成后,管理员创建用户,但无法设置用户密码,只有通过邮件由用户自行修改.无法适应某些没有统一邮箱的情况,现将关闭邮箱验证方法和管理员设置用户密码方法验证通过 不用邮件验证: c ...
- Oracle 性能优化 — 统计数据收集[Z]
ORACLE优化器的优化方式有两大类,即基于规则的优化方式(Rule-Based Optimization,简称为RBO)和基于代价的优化方式(Cost-Based Optimization,简称为C ...
- HTML meta refresh 刷新与跳转(重定向)页面
<meta http-equiv="refresh" content="5; url=http://www.cnblogs.com/" /> 可用于 ...
- Linux Mysql 总结
一:Error Code: . Access denied for user 'root'@'%' to database ① mysql -u root -p 进入到mysql中 ②SELECT h ...
- php curl函数实例
<?php function login(){ $url = 'http://jspatch.qq.com/offline/check?qver=6.2.0.427&hver=0& ...
- 【Chromium中文文档】Web安全研究
转载请注明出处:https://ahangchen.gitbooks.io/chromium_doc_zh/content/zh//General_Architecture/Extension_Sec ...