创建一个支持异步操作的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 ...
随机推荐
- Struts2 + Spring + hibernate 框架搭成实例
1.准备Jar包: struts2.hibernate.spring所需jar包 struts-core-2.x.x.jar ----struts核心包 xwork-core-2.x.x.jar ...
- 全国计算机等级考试二级教程-C语言程序设计_第14章_结构体、共用体和用户定义类型
函数的返回值是结构体类型 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> struct ...
- Mac 下 Chrome 快捷键大全
1. 标签页和窗口快捷键 ⌘-N 打开新窗口. ⌘-T 打开新标签页. ⌘-Shift-N 在隐身模式下打开新窗口. 按 ⌘-O,然后选择文件. 在 Chrome 浏览器中打开计算机中的文件. 按住 ...
- Exception Handling in ASP.NET Web API
public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErr ...
- iOS开发中视图相关的小笔记:push、modal、popover、replace、custom
在storyboard中,segue有几种不同的类型,在iphone和ipad的开发中,segue的类型是不同的. 在iphone中,segue有:push,modal,和custom三种不同的类型, ...
- MSDN地址,记录下来,以防以后使用
MSDN在线官网:https://msdn.microsoft.com/zh-cn/default.aspx 以备学习时候使用.
- 应用程序无法启动,因为应用程序的并行配置不正确,有关详细信息,请参阅应用程序事件日志,或使用命令行SxsTrace.exe工具
今天做项目,需要用C#引用C++的链接库文件,但是在调试的时候会报错这个错误. 运行SxsTrace.exe 以管理员用户登陆,启动cmd; 执行命令:SxsTrace Trace -logfile: ...
- Spring AOP那些学术概念—通知、增强处理连接点(JoinPoint)切面(Aspect)
1.我所知道的AOP 初看起来,上来就是一大堆的术语,而且还有个拉风的名字,面向切面编程,都说是OOP的一种有益补充等等.一下让你不知所措,心想着:管不得很多人都和我说AOP多难多难.当我看进去以后, ...
- Lazarus解决含中文文件名或路径的使用问题
其实用lazarus很久(也不算久啦..),目前打算做完手头的最后一个小程序然后就转向c#窗体程序..之前用lazarus的时候出了很多问题,资料也不是很好找,所以这回把比较容易说的记下来省得忘掉 ...
- 各种数据库的批量插入操作_Oracle
最近工作中需要优化以前各种的Excel批量导入功能,目前将能优化的方面做个记录. 选用技术: 目前.Net可以访问Oracle常用的Dll,有三种: 微软自带的 System.Data.OracleC ...