9

I've got a serious doubt. Suppose the following scenario:

  1. You have a UIViewController onscreen.
  2. The app initiates, say, a backend call using a block as a callback
  3. You use a 'self' surrogate, to prevent retain cycles.
  4. The user hits 'Back', and the UIViewController gets dealloc'ed.
  5. Sooner or later, the callback block gets executed >> BAD ACCESS

Before iOS 4, we dealt with this kind of situation by setting to nil the delegate property of... i don't know, whatever class you were using.

But nowadays... how do you cancel a block??. What if the block was sent to a static method, and you have no way of wiping out that callback reference??.

In that case, should we avoid using the 'self' surrogate?

BTW, by 'self' surrogate, i mean to say:

__block typeof(self) bself = self;

Thanks!!

8
  • 1
    How about setting the static block variable to NULL once the view is dealloc'd? Commented Jul 30, 2011 at 21:44
  • You mean, having a static __block variable (or a class member), and wiping it out ?. Commented Jul 30, 2011 at 22:05
  • 1
    I was hoping to find some sort of API to name & kill a block. But i didn't realize i could just have a __block as a class member. That's sooo much simpler... thanks Alex! Commented Jul 30, 2011 at 22:05
  • @AlexNichol, can you elaborate on setting this block variable to NULL? Where is the __block variable declared and how? Thanks Commented Jun 5, 2012 at 21:28
  • @bandejapaisa the idea was to maintain a static block variable, and set it to NIL once the object gets de-alloc'ed. Commented Jun 5, 2012 at 22:44

2 Answers 2

5

Well, first off: If (and only if) your reason for avoiding the use of self or direct access of ivars inside of a block really are retain-cycles, then you should be in a situation like

client => objectA => blockWithWeakBackReference

(where => means 'has a strong reference to').

In this case, blockWithWeakBackReference should only ever be invoked by objectA, so there is no danger of a BAD ACCESS.

If I understand your question correctly, what you really mean is a different scenario:

  • objectA wants some application-wide service to execute a block on its behalf, if some precondition is met.
  • You avoid using self inside of the block because you want to be able to dispose of objectA before the block is executed.

One example for this might be a shared network-queue that executes a block when the request finished loading for one reason or another.

In that case, I would suggest to simply copy the design of NSNotificationCenter's addObserverForName:object:queue:usingBlock: and make your service implement a pair of methods like -(SomeTokenObjectType)addWorkerBlock:(void(^)(whatever-signature-makes-sense-for-you)) and -(void)cancelWorkerBlockWithToken:(SomeTokenObjectType) in order to enqueue and cancel your callback-blocks.

Then, all objects that use this service can simply have an ivar of type NSMutableSet to store the token for every enqueued block and — in their dealloc — enumerate the remaining tokens, canceling them with the service.

Sign up to request clarification or add additional context in comments.

2 Comments

That's exactly the scenario i'm dealing with Dany. Thanks a lot for your comments, i really appreciate it. I'm gonna implement that mechanism, i really like the way it looks!.
It's been a while, but I wanted to add a maybe interesting bit of information for anyone stumbling over this to tackle related problems: In his formidable article Let's build NSNotificationCenter, Mike Ash implements this mechanism in a very clean and elegant way—so be a great artist and steal his approach ;-)
0

"to prevent retain cycles."

But do you really have a retain cycle to prevent? Think about this. The block retains self (your view controller). The backend call retains the block. But where does self retain the block?

5 Comments

If you don't use a self surrogate in (3), the current UIViewController will get retained, till you get a response from the backend.
Which is not cool at all. If the user hits 'back', the current viewController should get released. The 'retain cycle' part depends on your communications layer... theoretically, you're right. You shouldn't get a retain cycle, if the code is well written. Yet, you'll be using more resources than what you actually need.
@JorgeLeandroPerez: Yes, it will get retained until the backend task finishes. But c'mon, this is not a big deal. It's not true that you know a view controller will be deallocated when it is popped; other things in the OS could still be retaining for various reasons.
Anyway, unless there's a way to cancel the backend task, the backend task must retain the thing it calls back to. So you must either retain this UIViewController, or, if you are really strict about the memory usage of this view controller, you can have the block instead retain a (small) dummy proxy object, and your view controller can be the delegate to this dummy object, and it will nil the delegate when it deallocates.
This is quite an old question. What i've been doing, nowadays, is simply what you say. Unless few specific cases, in which memory usage IS big deal. In those cases, i just store the active requestId's, and cancel them before going back. Thanks for your help!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.