3

Lets say we have NSOperation A and NSOperation B. B is dependant on A finishing and performing some setup as a result of A.completionBlock being called and finished. This means that B.addDependency(A) cannot be used as B cannot be constructed until A has finished. Therefore we opt to use A.waitUntilFinished() on a separate thread before constructing and starting B.

However, because the completionBlock of A is called on a different thread, the thread that we call A.waitUntilFinished() unblocks before A.completionBlock has finished executing and hence on construction of B the necessary prerequisites have not been completed.

Because the apple NSOperation API does not provide any control over where A.completionBlock is dispatched, what is the usual way to handle this issue?

Edit:

The option I've tried so far is to wrap NSOperation B in an NSBlockOperation C and then call C.addDependency(A) so that B isn't constructed until A is finished. However this still doesn't solve the problem as asynchronous start must still be called within NSBlockOperation C and the completion block still flies off to another thread.

Michael
  • 2,258
  • 1
  • 23
  • 31
  • Is it the case that some of your code in A.completionBlock should actually be going in NSOperation A itself? – occulus Nov 05 '13 at 12:17
  • can you just call B.start at the end of A.completionBlock? – lead_the_zeppelin Nov 05 '13 at 12:17
  • NSOperation A saves Model 1 to a relational database which then returns a unique identifier for that model. Model 1 is related to Model 2 which is saved by NSOperation B. B needs to send the unique identifier of Model 1 to the database to form this relation. Placing code from `A.completionBlock` in A would be coupling two APIs that shouldn't really be coupled. Calling `B.start()` from `A.completionBlock` isn't an option either, as the `save()` method of the models are independent of each other. – Michael Nov 05 '13 at 12:33
  • 2
    See http://stackoverflow.com/questions/18745635/when-will-completionblock-be-called-for-dependencies-in-nsoperation – Rob Nov 05 '13 at 12:52
  • @Michael, now I understand what you was looking for when you wrote wrote your comments in my topic about NSOperation internals. My topic is really not related to yours. Could you please update your question with a code (pseudocode or shortened variant maybe) so it would be possible to understand how you try to pass your unique indentifier from `Operation1's completionBlock` to `Operation2`? – Stanislav Pankevich Nov 05 '13 at 21:48
  • Do I understand right, that you question could be rephrased like this: how a data we've got upon completion of Operation1 (your "unique identifier") could be passed to Operation2, that has the Operation1 as its dependency? – Stanislav Pankevich Nov 05 '13 at 21:49
  • Hi Stanislaw - it can indeed be rephrased like that. – Michael Nov 07 '13 at 12:00
  • The relation of your topic to mine was that I was attempting to understand the NSOperation internals (specifically the invocation of completionBlock) by the internal KVO method that I noticed in the call stack in the same way you did. – Michael Nov 07 '13 at 12:00
  • I have ended up implementing the Promise pattern in a similar fashion to what was described in the thread that Rob posted. – Michael Nov 07 '13 at 12:01

2 Answers2

0

Since what you want to happen is to have Model 2 save once Model 1's uuid is known, can you express it in those terms, and have Model 2 observe the uuid of Model 1 (or use a notification) and initiate operation B once it has a value set?

Airsource Ltd
  • 32,379
  • 13
  • 71
  • 75
0

I think you went on a trail that leads to hard-to-solve edge issues.

I can't see why any B Action cannot be "constructed" before A finishes - your B Operation can use whatever logic it wants inside it, including - but not limited to - constructing and scheduling new operations, depending on the outcome of A.

The completion blocks were NEVER meant to be means for managing your queue's order of execution, and that's why they are "loosely bound" to the main flow and run in unknown external threads. Just don't use them.

If you need REAL dependency mechanism - one that ensures B will only run if A completed its work successfully, and that B will be cancelled if A is cancelled (didn't complete successfully) then out-of-the-box NSOperation classes can't give you that - they were designed differently.

I had to do this for my own work, and I posted a question: Can NSBlockOperation cancel itself while executing thus canceling dependent NSOperations? There is great answer there by someone, but also the solution I found which works for me fantastically for several years now.

Motti Shneor
  • 2,095
  • 1
  • 18
  • 24