Magic of NSOperation internals - how does it observe isFinished key so that completionBlock is always run?

守給你的承諾、 提交于 2019-12-10 10:01:49

问题


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.


回答1:


This is what I currently know about NSOperation's lifecycle, it is not much but is pretty enough to answer my original question:

  1. 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).

  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!