Search code examples
iosobjective-cautomatic-ref-countinguialertviewobject-lifetime

Strong reference to `self` to keep the object alive (temporarily): evil?


I'm creating a wrapper for UIAlertView (I know about UIAlertController and about several already existing wrappers, it's also for educational purposes).

Suppose it looks like this (very shortened version):

@interface MYAlertView : NSObject
-(void)show;
@end

@interface MYAlertView()<UIAlertViewDelegate>
@end

@implementation MYAlertView
-(void)show {
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Some title"
                                                        message:@"Some message"
                                                       delegate:self 
                                              cancelButtonTitle:@"Cancel"
                                              otherButtonTitles:nil];

    [alertView show]
}

#pragma mark UIAlertViewDelegate implementation

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    //Do something.
}
@end

And, for instance, I use it like this:

// USAGE (inside some ViewController)
-(void)showMyAlert {
    dispatch_async(dispatch_get_main_queue(), ^{
        MYAlertView *myAlertView = [[MYAlertView alloc] init];
        [myAlertView show];
    });
}

The problem I have is the following:

  1. [myAlertView show] causes the alertView to appear. myAlertView is set as a delegate of the alertView.
  2. There is the only strong reference to myAlertView: inside the block in the showMyAlert method. When it's finished, myAlertView is deallocated.
  3. When the user clicks a button on the alertView, the alertView calls it's delegate method, but the delegate (myAlertView) is deallocated already, so it causes BAD_ACCESS (the delegate in UIAlertView is declared as assign, not weak).

I want to make MYAlertView as easy to use as it is with UIAlertView, so I don't want to make the user store a strong reference to it somewhere (it is inconvenient).

So, I have to keep the myAlertView alive as long as the alertView is shown somehow. The problem is I can't think of any way other than creating a strong reference inside MyAlertView, assigning it to self, when I show the alertView, and assigning it to nil, when I dismiss it.

Like so (only the changed bits):

@interface MYAlertView()<UIAlertViewDelegate>
//ADDED:
@property (nonatomic, strong) id strongSelfReference;
@end

@implementation MYAlertView
-(void)show {
    UIAlertView *alertView = [[UIAlertView alloc] init /*shortened*/];
    [alertView show]

    //ADDED:
    self.strongSelfReference = self;
}

#pragma mark UIAlertViewDelegate implementation
//ADDED:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
    self.strongSelfReference = nil;
}
@end

It should work: the moment the alertView is dismissed, the strongSelfReference will be set to nil, there will be no strong references left to the myAlertView, and it will get deallocated (in theory).

But keeping a strong reference to self like this looks evil to me. Is there a better way?

UPDATE: The MYAlertView in reality is an abstraction layer around the now deprecated UIAlertView and a new UIAlertController (iOS 8+), so subclassing UIAlertView is not an option.


Solution

  • Yes, your object should keep a strong reference to itself. It's not evil to do so.

    A self-reference (or, in general, any reference cycle) is not inherently evil. The evil comes in creating one unintentionally such that it is never broken, and thus leaks objects. You're not doing that.