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