Search code examples
objective-ciosipadmemory-managementuipopover

Memory Crash in UIPopoverController


I've now invested days in trying to figure out what is going on and for the life of me I can't see what I am doing wrong. I am popping up a UIPopover when the user touches a point on the screen. The popover has a tab controller and table view that displays information about that point. But when the popover is dismissed, it crashes claiming that: -[UIAnimator removeAnimationsForTarget:]: message sent to deallocated instance

Here is the code that loads the view controller:

MyViewController *popView = [[MyViewController alloc] init];
myPop = [[UIPopoverController alloc] initWithContentViewController:pop];
[popView release];
myPop.delegate = self;
[airportPop setPopoverContentSize:popView.view.frame.size];
[airportPop presentPopoverFromRect:CGRectMake(location.x,location.y,1,1) inView:self.mainView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

- (void)dismissPopover {
    if( myPop != nil ) {
        [myPop dismissPopoverAnimated:YES];
        [myPop.delegate popoverControllerDidDismissPopover:airportPop];
    }
}

- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
    [myPop release];
    myPop = nil;
}

The actual MyViewController is just a UIViewController that with (abridged for brevity) init:

  - (id)init
{
    self = [super init];
    //create a newview 
    self.view = popView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, POPUP_WIDTH, POPUP_HEIGHT)];
    [popView release];

    topBar = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, POPUP_WIDTH, 30)];
             ....
    [popView addSubview:topBar];
    [topBar release];

    //create a table view 
    self.table = [[UITableView alloc] initWithFrame:CGRectMake(0, 30, POPUP_WIDTH, POPUP_HEIGHT-30-49)];
        table.delegate = table.dataSource = self;
         ....

    //create a tab bar 
    tabBar = [[UITabBar alloc] initWithFrame:CGRectMake(0, POPUP_HEIGHT-49, POPUP_WIDTH, 49)];
    tabBar.delegate = self;

    [popView addSubview:tabBar];
    [popView addSubview:table];

    [tabBar release];
    [table release];
    return( self );
}

Dealloc is nothing more than [super dealloc] since everything is essentially owned by the view and the view controller will take care of it. When the myPop is released, in DidDismissPopover, the view is also released, so that seems to work okay. But very soon thereafter, I get the crash.

Do I need to do something special to discard the tab view or table view when the popup dismisses?

I am using an autorelease on the cells in the table, should I stop doing that?

        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];

Thank you in advance for any help!!! Any ideas at all are greatly appreciated!!

-Kevin


Solution

  • [myPop dismissPopoverAnimated:YES] will continue to access you object even after the method call because you set YES for the animation (there is a timer and other stuff going under the hood to perform the animation for that)

    So, instead of immediately releasing the object, you could mark it as autorelease to postpone this action, which actually might solved it or not.

    Or postpone the release to a time after that makes tyou sure thta the animation will be finished. You could use GCD for that (if you are using iOS 4+) and as the default time for animation in UIKit is 0.3s, the code bellow should do the trick.

    double delayInSeconds = 0.3;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [myPop.delegate popoverControllerDidDismissPopover:airportPop];
    });
    

    EDIT: You should use this time only for the test propose as it is far from being the right way to release an object.

    You should store a pointer to your UIPopover and release it in your class dealloc method.