你的位置:首页 > 操作系统

[操作系统]创建一个支持异步操作的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