Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS and ARC : How to retain self during asynchronous operations?

It's the first time I'm fiddling around with iOS5 and ARC. So far, so good, it works, but I've run into some kind of a problem.

I have a custom UIStoryboardSegue in which I use Facebook Connect (or other services) to log the user into my app. Simply put, it should do the following :

  1. User clicks on the button
  2. Button fires the Segue
  3. The segue starts asynchronous login but does not immediately push the view controller
  4. If, and only if the login is successful, the segue pushes the view controller

What happens instead, is that the login starts, but the segue is immediately released by ARC before it has any chance to complete.

I thought of a quick'n'dirty hack to prevent this:

@interface BSLoginSegue() {
    __strong BSLoginSegue *_retained_self;
}
@end

// Stuff...
// Other stuff...

- (void) perform {
    login();
    _retained_self = self;
}

- (void) loginServiceDidSucceed:(BSLoginService *)svc {
    ...
    _retained_self = nil;
}

The thing is, it really is a hack, so I was wondering if there were any other, and more elegant way I could do the same?

like image 506
F.X. Avatar asked May 09 '12 15:05

F.X.


2 Answers

The idea that a class should need to retain itself suggests that there might be a design problem. When an object retains itself, it becomes unclear who owns it or who might have a reference to it, and the risk of a leak is high.

What class is responsible for presenting the segue? Is it the same class the contains the button? This class should retain the segue, present the segue, and then release the segue when/if the segue completes.

Without further insight into the view controller hierarchy, it's hard to give specific advice. But my first reaction is to suggest that the view controller that is deciding to present the segue or not should have a strong property on the segue. The subclassed segue might define a protocol that the presenting class may conform to to be advised as to when the segue should be nilled/released.

like image 25
isaac Avatar answered Nov 13 '22 08:11

isaac


If you use Grand Central Dispatch (GCD) for concurrency (which you should, it's awesome!) then by putting a reference to your object in an Objective-C block and passing it into GCD, it will be automatically retained by the block until after the block has executed and itself been released.

Without knowing exactly how you're doing your async operations it's hard to give a definitive answer, but by using blocks and GCD you won't have to worry about this at all. The block will take care of retaining objects it references and ARC will do the rest.

It looks like you must be storing a reference to your UIStoryboardSegue subclass somewhere, in order to call loginServiceDidSucceed: on it, so perhaps just making that a strong reference will work. Once again, referencing it from a block (such as a completion block when the login succeeds) is ideal. I would recommend adapting your login code to use blocks for the success/failure callbacks.

like image 50
jhabbott Avatar answered Nov 13 '22 10:11

jhabbott