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 did some experiments with my subclasses of NSOperation - I tried to add my own observers to the isFinished property, and they worked well as expected.
These experiments made me wonder how NSOperation calls it completionBlocks based on its observation of changes to the isFinished properties -
What I do not understand and why this question is that my observers from the isFinished property never interfere with NSOperation (if I add them, delete them ...), so the logic is observe isFinished -> invoke completionBlock when it becomes YES encapsulated pretty well, giving me the freedom to do extra KVO observations without any problems:
1) I did a couple of tests showing that NSOperation does some kind of magical subscription to observe property changes right in my -[NSOperation init] - I donβt know what is going on there, but I made sure that it has something to do with "isFinished-> completeBlock" goes there . I wonder what was done there, except for the general - [NSObject init] logic?
2) The NSLog output shows that it is not an NSOperation class, but calls some mysterious NSOperationInternal with observeValueForKeyPath:ofObject:changeKind:oldValue:newValue:indexes:context: which finally causes the completion.
3). As far as my understanding can go, the GNUStep implementation of NSOperation differs in implementation details (at least in the described isFinished-completionBlock , see, for example, _ completion method ), so I can not use it as an assistant to understand Apple's approach to that as NSOperation is written.
NB
I have no problems, I just want to understand more deeply how NSOperation works internally in terms of isFinished observing -> completionBlock invocation .
I donβt want to see: "The internal elements of Apple are hidden, it is impossible to find out by non-Apple engineers." I want to see an answer that contains a deep understanding of this topic.