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

// 触发isFinished属性的KVO观察者,这样系统就知道这个operation已经执行完毕
    [self willChangeValueForKey:@"isFinished"]; 
self.state
= MyOperationStateFinished;
[self didChangeValueForKey:
@"isFinished"];
[self.
lock unlock];
}
@end

 

智能推荐

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告