最近做了一些和 NSOperation 有关的事情,感觉这个类还是挺有意思的,这里记录一下细节和遇到的一些坑。
我们一般使用 NSOperation 的时候,通常会配合 NSOperationQueue 来进行管理,通过配置 NSOperationQueue 的 maxConcurrentOperationCount
来设置为并行或者串行。而对于一个 NSOperation 来说,我们也可把它处理成同步或异步的,针对不同的情况,我们也需要做不同的处理。
我们打开 NSOperation.h 会首先看到 start
方法和 main
方法。接下来我就详细说下自己的理解。
Concurrency Programming Guide
main 方法
看文档的注释发现,NSOperation 的默认实现是什么都没做,也就是个空方法,当子类去重写的时候,也不用调 super,而且它会在一个由 NSOperation 提供的 autorelease pool
中执行,子类也不需要创建自己的 autorelease pool
,
当我们实现一个同步
的方法的时候,我们只需要重写 main
函数,处理我们的业务逻辑即可,不需要处理除 canceled
之外的任何状态。关于 canceled
,后边再说。
start 方法
这个方法默认实现是处理了一些状态,检查这个 operation 是否可以正常执行,如 ready 是否为 YES, cancelled 是否为 YES, finished 是否为 YES ,然后调用 main 方法。如果不满足条件,直接 return,就结束了。
如果要执行一个异步
的 Operation,例如发送一个网络请求,那么,需要重写 start
方法,并自己手动设置一些状态(不能调用 super),处理这些状态的时候,需要手动触发一些 KVO 来通知其他可能监听的对象,如 isCancelled, isExecuting, isFinished 等。当异步处理结束时,需要更新 finished 和 executing 状态。
如果是异步的话,我们也应该在 处理 asynchronous 属性,返回 YES
cancelled
我们应该一直关注 canceled 状态,当被标记为 YES 的时候,我们需要取消我们现在所执行的任务,而 不只是
在 start
或 main
函数中对 canceled
状态进行判断。当有一个循环,或者说是耗时比较长的操作中,我们应该在比较好处理的位置,判断 canceled 状态,在 YES
的情况下,结束我们的 operation。
- 如果是 YES 的话,应该立即结束
- 在每个循环中,至少判断一次,如果一个迭代比较耗时,建议多检查几次
- 在任何处理比较方便的位置,取消任务
说了那么多,那我们看看如何定义一个子类,来处理同步和异步
同步
1 2 3 4
| @interface MySyncOperation : NSOperation
@end
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @implementation MySyncOperation
- (void)main { }
- (BOOL)isAsynchronous { return NO; }
@end
|
异步
1 2 3 4
| @interface MyAsyncOperation : NSOperation
@end
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| @interface MyAsyncOperation () { BOOL executing; BOOL finished; BOOL cancelled; }
@end
@implementation MyAsyncOperation
- (void)start { if (!self.cancelled) { [self setOperationToExecutingState]; [self handleBusiness]; } else { [self setOperationToFinishState]; } }
- (void)cancel { if (self.isFinished) { return; } [super cancel]; [self setOperationToCancelState]; }
- (void)handleBusiness {
}
- (void)setOperationToExecutingState { [self willChangeValueForKey:@"isFinished"]; [self willChangeValueForKey:@"isExecuting"]; executing = YES; finished = NO; [self didChangeValueForKey:@"isExecuting"]; [self didChangeValueForKey:@"isFinished"]; }
- (void)setOperationToFinishState { [self willChangeValueForKey:@"isFinished"]; [self willChangeValueForKey:@"isExecuting"]; executing = NO; finished = YES; [self didChangeValueForKey:@"isExecuting"]; [self didChangeValueForKey:@"isFinished"]; }
- (void)setOperationToCancelState { [self willChangeValueForKey:@"isCancelled"]; cancelled = YES; [self didChangeValueForKey:@"isCancelled"]; }
- (BOOL)isExecuting { return executing; } - (BOOL)isFinished { return finished; }
- (BOOL)isCancelled { return cancelled; }
- (BOOL)isAsynchronous { return YES; } @end
|