UIViewController Retaining in ARC

97 views Asked by At

I have a subclass of UIViewController -> MyPopUpViewController

@protocol MyPopUpViewController Delegate;
@interface MyPopUpViewController : UIViewController
{

}
@property (nonatomic, strong) id <MyPopUpViewControllerDelegate>  delegate;

-(IBAction) buttonPressed:(id)sender;
@end

@protocol MyPopUpViewControllerDelegate
-(void) popupButtonPressed: (MyPopUpViewController*)controller;
@end

I cannot have this MyPopUpViewController as an instance variable because this comes externally, and there could be many and multiple of these popups can be up. So far I tried this, and it crashes on the delegate call due to not being retained:

MyMainViewController:

-(void)externalNotificationReceived: (NSString*) sentMessage
{    
    MyPopUpViewController *popupView = [[MyPopUpViewController alloc] init];
    popupView.delegate = self;

    [self.view addSubview:popupView.view];

    [popupView setInfo :sentMessage :@"View" :@"Okay"];
    popupView.view.frame = CGRectMake(0, -568, 320, 568);
    popupView.view.center = self.view.center;
}

-(void)popupButtonPressed:(MyPopUpViewController *)controller :(int)sentButtonNumber
{
    NSLog(@"Popup Delegate Called");

    [controller.view removeFromSuperview];
    controller.delegate = nil;
    controller = nil;
}

Once the popup comes up, and when the ok button is tapped, it crashes and never gets to that NSLog. How can I change

MyPopUpViewController *popupView = [[MyPopUpViewController alloc] init];

..so it would retain without making it an instance variable?

Thanks in advance.

2

There are 2 answers

2
rmaddy On BEST ANSWER

You should be doing proper view controller containment by calling addChildViewController:.

- (void)externalNotificationReceived: (NSString*) sentMessage {    
    MyPopUpViewController *popupView = [[MyPopUpViewController alloc] init];
    popupView.delegate = self;

    [popupView setInfo :sentMessage :@"View" :@"Okay"];
    popupView.view.frame = CGRectMake(0, -568, 320, 568);
    popupView.view.center = self.view.center;

    [self addChildViewController:popupView];    
    [self.view addSubview:popupView.view];
    [popupView didMoveToParentViewController:self];
}

This will keep a proper reference to the view controller as well as properly pass various view controller events. Read about this in the docs for UIViewController and the "View Controller Programming Guide for iOS".

BTW - you should name your methods better. Example:

popupButtonPressed::

should be named:

popupButtonPressed:buttonNumber:
0
MaddTheSane On

Usually delegates are weak-referenced instead of strong. I, myself, would name it something else as to not confuse other people.

Also, the following bit of code will have no effect:

-(void)popupButtonPressed:(MyPopUpViewController *)controller :(int)sentButtonNumber
{
    ...
    controller = nil;
}

the controller would be released (set to nil) automatically at the end of the scope.