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
原标题:创建一个支持异步操作的operation
关键词:异步