iOS NSOperation 源码分析原理
特性
1.添加任务依赖
2.任务执行状态的控制
3.设置操作的优先级
4.设置最大并发量
5.可以取消一个操作
6.使用 KVO 观察对操作执行状态的更改:isExecuteing、isFinished、isCancelled
任务状态的控制
1.isReady
2.isExecuting
3.isFinished
4.isCancelled
5.如果我们重写main方法,底层会为我们控制任务的执行状态,以及任务的退出
6.如果我们重写start,需要自己控制任务状态。
源码分析 (gnustep-base-1.26.0 版本)
我们下载gnustep-base-1.26.0的源码,然后找到 NSOperation.m,文件,然后会看到有start方法
- (void) start
{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
double prio = [NSThread threadPriority];
AUTORELEASE(RETAIN(self)); // Make sure we exist while running.
[internal->lock lock];
NS_DURING
{
if (YES == [self isExecuting])
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] called on executing operation",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
if (YES == [self isFinished])
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] called on finished operation",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
if (NO == [self isReady])
{
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] called on operation which is not ready",
NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
}
if (NO == internal->executing)
{
[self willChangeValueForKey: @"isExecuting"];
internal->executing = YES;
[self didChangeValueForKey: @"isExecuting"];
}
}
NS_HANDLER
{
[internal->lock unlock];
[localException raise];
}
NS_ENDHANDLER
[internal->lock unlock];
NS_DURING
{
if (NO == [self isCancelled])
{
[NSThread setThreadPriority: internal->threadPriority];
[self main];
}
}
NS_HANDLER
{
[NSThread setThreadPriority: prio];
[localException raise];
}
NS_ENDHANDLER;
[self _finish];
[pool release];
}
我们来分析下,首先我们可以看到,创建了一个 autoReleasePool,然后获取线程的优先级,接着加了锁,接着是一堆判断,我们先不关心,然后我们可以看到
if (NO == internal->executing)
{
[self willChangeValueForKey: @"isExecuting"];
internal->executing = YES;
[self didChangeValueForKey: @"isExecuting"];
}
如果当前没有在执行,则通过 KVO 的方式来通知当前正在执行了,什么事KVO,可以看我之前的文章,接下来
if (NO == [self isCancelled])
{
[NSThread setThreadPriority: internal->threadPriority];
[self main];
}
这里,判断如果没有取消,那么久执行main函数,然后我们看下 main 函数
- (void) main;
{
return; // OSX default implementation does nothing
}
所以从源码可以看出,我们只需要重写 main 函数来执行我们的任务就行了,再接着往下看
[self _finish];
这个finish做了什么呢?
- (void) _finish
{
/* retain while finishing so that we don't get deallocated when our
* queue removes and releases us.
*/
[self retain];
[internal->lock lock];
if (NO == internal->finished)
{
if (YES == internal->executing)
{
[self willChangeValueForKey: @"isExecuting"];
[self willChangeValueForKey: @"isFinished"];
internal->executing = NO;
internal->finished = YES;
[self didChangeValueForKey: @"isFinished"];
[self didChangeValueForKey: @"isExecuting"];
}
else
{
[self willChangeValueForKey: @"isFinished"];
internal->finished = YES;
[self didChangeValueForKey: @"isFinished"];
}
if (NULL != internal->completionBlock)
{
CALL_BLOCK_NO_ARGS(internal->completionBlock);
}
}
[internal->lock unlock];
[self release];
}
这段代码很容易理解,就是通过KVO来设置 executing 为 NO, finished 为YES。
所以从上面 NSOperation.m 的源码可以看出,如果我们重写了 Main ,系统会为我们设置一系列的属性,并且通过KVO通知,但是如果我们重写了Stat方法,相当于,重写了上面的源码,所以任何属性的控制都需要我么自己来控制
系统是怎么帮我们从 NSOperationQueue中移除任务的呢?
上面我们已经说了,系统帮我们控制了属性的变更,通过KVO,那么NSOperationQueue,就一定是通过监听KVO来做到的,我们通过查看源码,果然,在下面 有个分类
@implementation NSOperationQueue (Private)
- (void) observeValueForKeyPath: (NSString *)keyPath
ofObject: (id)object
change: (NSDictionary *)change
context: (void *)context
并且监听了KVO,系统就是在这个方法里面为我们移除任务的
通过对源码的分析,是不是豁然开朗了?
网友评论