Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Great work! #4

Open
malhal opened this issue Mar 24, 2016 · 3 comments
Open

Great work! #4

malhal opened this issue Mar 24, 2016 · 3 comments

Comments

@malhal
Copy link

malhal commented Mar 24, 2016

But could I suggest a small improvement: in the finish method, inside the perform block, if it called a new method finishOnDispatchQueue to set the state instead of setting it directly, then that method could be overridden by subclasses and allow them to call their own custom completion block and then calling super to change the state and end the operation. This way they can guarantee their completion block is called on the dispatch queue using your nifty code.

@malhal
Copy link
Author

malhal commented Mar 24, 2016

Oh and finish should probably take an NSError param, e.g. finishWithError and also pass it to finishOnDispatchQueueWithError I mentioned. This way the custom completion blocks can also supply an error param.

@dmcrodrigues
Copy link
Owner

Thanks @malhal

I analysed your suggestion about creating a new method finishOnDispatchQueue but I'm not seeing a good motive to do that kind of exposure.

This way they can guarantee their completion block is called on the dispatch queue using your nifty code.

Why do you think is important guarantee the invocation of completion block on the dispatch queue used internally? I'm asking that because the only purpose of that queue is change the internal state of the operation in a safely way.

Another question is if we really need that explicit completion block or if we can rely on the completionBlock from NSOperation's which is always executed after the operation has completed even when it was cancelled.

Let me know your thoughts about this, I'm not against the change but currently I'm not seeing a strong reason to do that change.

@malhal
Copy link
Author

malhal commented Mar 30, 2016

Hi thanks for taking the time to think about this! Very nice of you.

I began writing an example to demonstrate the problem and I couldn't produce a concrete issue so I thought it would be better just to answer the questions, in reverse order if you don't mind.

NSOperation's completionBlock and the next NSOperation in the queue run simultaneously. This means if using dependent operations it isn't possible to initialise a property on the next operation on the completion of the first, because it has already started. So we can't use that. Anyway we would want our own block so we can pass the result and error.

Given this, we then have to resort to our completion block running on our own background thread we created in the async task. From there it's more tricky to explain why I think it would be better for a custom completion block to run on the dispatch queue instead of on that but I have some guesses. Maybe it would ensure safe manipulation of the operations running on the queue. Since say for example you get an error so in your completion block you make make the queue cancel all operations and if an operation overrides cancel, it is safer to be on the dispatch queue then. Also any calls to operation properties like isFinished result in a dispatch sync to the dispatch queue which seem unnecessary given the operation is about to finish. Maybe also to ensure the operation finishing is the next thing after the completion block ends without a delay to sync with the dispatch queue. Lastly I've been reverse engineering the CloudKit iOS API and they do it this way, then again they call the main from their dispatch queue so maybe that's their reason!

@interface TestOperation : CKQueryOperation
@end

@implementation TestOperation
-(void)main{ // 12
    dispatch_queue_t d = dispatch_get_current_queue();   
/*
<OS_dispatch_queue: com.apple.cloudkit.operation.callback[0x7fe5c1d0f290] = { xrefcnt = 0x2, refcnt = 0x2, suspend_cnt = 0x0, locked = 1, target = com.apple.root.default-qos.overcommit[0x10852b300], width = 0x0, running = 0x0, barrier = 1 }>
*/

    [super main];
}
@end

Methods of interest on CKOperation:

    NSObject<OS_dispatch_queue> *_callbackQueue;

- (void)_finishOnCallbackQueueWithError:(id)arg1;
- (void)finishWithError:(id)arg1;
- (void)_finishInternalOnCallbackQueueWithError:(id)arg1;
- (void)_handleCompletionCallback:(id)arg1;
- (void)_handleProgressCallback:(id)arg1;

So that's my thoughts sorry if I was vague on the last answer and I understand if you don't see a strong reason in it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants