Demonstration
- (void)test_NSOperationCallsCompletionBlockWhenFinished {
__block BOOL flag = NO;
NSOperation *operation = [NSOperation new];
operation.completionBlock = ^{
NSLog(@"Hunting NSOperation internals: %@", [NSThread callStackSymbols]);
flag = YES;
};
[operation start];
while (flag == NO);
STAssertTrue(flag, nil);
}
Gives me the following input:
2013-07-28 19:59:44.690 SACompositeOperationsApp[99551:3103] Hunting NSOperation internals: (
0 SACompositeOperationsApp 0x000000010005bbd9 __68-[SAOperationTests test_NSOperationCallsCompletionBlockWhenFinished]_block_invoke + 41
1 Foundation 0x00007fff8a27bb25 __+[__NSOperationInternal _observeValueForKeyPath:ofObject:changeKind:oldValue:newValue:indexes:context:]_block_invoke_3 + 55
2 libdispatch.dylib 0x00007fff8abd9a82 _dispatch_call_block_and_release + 18
3 libdispatch.dylib 0x00007fff8abdb083 _dispatch_async_f_redirect_invoke + 112
4 libdispatch.dylib 0x00007fff8abda961 _dispatch_worker_thread2 + 255
5 libsystem_c.dylib 0x00007fff91ce13da _pthread_wqthread + 316
6 libsystem_c.dylib 0x00007fff91ce2b85 start_wqthread + 13
)
Background
I was doing some experiments with my custom NSOperation subclasses - I was trying to add my own observers on isFinished
property and they worked well as expected.
These experiments made me suprised about how NSOperation calls its completionBlocks
based on its observation of isFinished
property changes -
The thing I don't understand and that's why this question is that mine observers of isFinished
property never interfere with NSOperation's ones (if I add them, remove them...), so that the observe isFinished -> invoke completionBlock when it becomes YES
logic is encapsulated pretty well giving me a freedom to do additional KVO observing without any problems:
1) I did a couple of tests showing me that NSOperation does some kind of magic subscription to property changes observation right in its -[NSOperation init]
- I don't know what is going on there but I have ensured that it is something related to "isFinished->completionBlock" is going there. I wonder what is done there besides common -[NSObject init] logic?
2) The NSLog output shows that it is not the NSOperation class but some mysterious NSOperationInternal is called with observeValueForKeyPath:ofObject:changeKind:oldValue:newValue:indexes:context:
that finally invokes completionBlock.
3) As far as deep my understanding can spread, the GNUStep implementation of NSOperation differs in implementation details (at least in the described isFinished-completionBlock
aspect, see, for example, _finish method), that is why I can't use it as a helper to understand the Apple's approach to how NSOperation is written.
N.B.
I don't have any problems unsolved, I just want to have a deeper understanding of how NSOperation works internally in the aspect of isFinished observing -> completionBlock invocation
.
I don't want to see: "Apple's internals are hidden, it is impossible to be known by non-Apple engineers". I want to see an answer that does contain a deep insight into this topic.
This is what I currently know about NSOperation's lifecycle, it is not much but is pretty enough to answer my original question:
NSOperation registers KVO observation of its
isFinished
property in its -init method. It means that the observation is registered even if I don't ever run-start
(main
) method (I did some experiments with custom NSOperation subclass to determine that). Corresponding un-registration is done in NSOperation's-dealloc
method (I can't prove that, but it is really the only place where it can happen).To make KVO possible and "private" NSOperation has some internal container class NSOperationInternal which encapsulates NSOperation's own KVO routines giving me a freedom to do my custom KVO when I want implement it for the instances of my custom subclasses of NSOperation class. In my custom NSOperation subclasses I can use my own implementation of
observeValueForKeyPath:ofObject:change:context:
method without worrying about any possible conflicts with NSOperation's own KVO because it would not interfere with this method implemented in NSOperationInternal.
P.S. Regarding the word 'magic' - I know it is all KVO behind the curtains - so no surprises here about KVO itself. My surprises are how KVO is applied to NSOperation in concrete details: nothing magical is here, but it was not really obvious, when I first began introducing my own KVO and became suspicious about possible interference.
来源:https://stackoverflow.com/questions/17910902/magic-of-nsoperation-internals-how-does-it-observe-isfinished-key-so-that-comp