Create an operation that supports asynchronous operations and an Asynchronous operation
NSOperationQueue is a common task scheduling mechanism in iOS. When creating a complex task, do we usually need to write? NSOperation subclass. In most cases, rewrite the main method to meet the requirements. After the main method is executed, the system will think that the operation is complete.
Sometimes the situation is not that simple. We need to call the asynchronous API in operation. This API will notify us of the result through a block or proxy. In this case, only the main method can be overwritten. Because the asynchronous API has not been executed, operation is completed ahead of schedule.
How can this problem be solved? I think AFNetworking has the same case, so I designed an asynchronous Task-based operation based on the implementation.
We need to overwrite the start method. The function of this method is somewhat similar to the main method, where the code logic of the task is implemented. How does the system know that our task is being executed or completed? The system listens to some operation attributes in the form of KVO. We can re-implement these attributes to notify the System of the task execution status. Important attributes include:
@property (readonly, getter=isReady) BOOL ready;@property (readonly, getter=isExecuting) BOOL executing;@property (readonly, getter=isFinished) BOOL finished;
They are all read-only attributes. We can simply rewrite them and return the values we want. But how can we notify the system that their values have changed? If you know KVO, you should know that the NSObject method can help us, and you can use them to manually notify the system that a certain attribute has changed.
- (void)willChangeValueForKey:(NSString *)key;- (void)didChangeValueForKey:(NSString *)key;
The following is the complete code. Here we only use an NSTimer to simulate an asynchronous task. When the state changes, we need to notify the KVO system that the operation status has changed. This step is very important. I ignored manual notifications to KVO at the beginning, causing tasks to never be completed (even if all the tasks in start are completed ).
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