Dismissal of Popover, [UIPopoverController dealloc] reached while popover is still displayed

I have a UIPopoverController stored in a strong property in my view controller. When the user rotates the iPad when a popover is displayed, I reject the popover and set my property to nil.

if (self.popover != nil) { [self.popover dismissPopoverAnimated:NO]; self.popover.delegate = nil; self.popover = nil; } 

When the code hits self.popover = nil, ARC tries to disable the UIPopoverController, but it crashes because it is supposedly still displayed.

How should I fire and release a parrot without breaking it?

+6
source share
3 answers

Firstly, it would be advisable to check if a popover is displayed, it is also convenient to check if it was highlighted:

 if ([self.popover isPopoverVisible]) { [self.popover dismissPopoverAnimated:NO]; } 

Now the problem is that you are not getting the delegate callback - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController if you programmatically drop the popover like this, but you need a strong popover link until it is no longer visible.

The way to do this is to set a delay for the nil property until you return to the main run loop, since when you return to the main run loop, all animations will be completed, and thus the popover will no longer be visible.

You need to move the code setting popover to nil to another method:

 - (void)releasePopover { self.popover.delegate = nil; self.popover = nil; } 

Then, in the rotation callback, add this method to start in the main run loop, I would like to do this by adding a call operation to the main run loop:

 if ([self.popover isPopoverVisible]){ [self.popover dismissPopoverAnimated:NO]; NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(releasePopover) object:nil]; [[NSOperationQueue mainQueue] addOperation:invocationOperation]; } 

Finally, for cleanliness, you'll probably want to call -releasePopover from your callback - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController .

So, all together:

 - (void)releasePopover { self.popover.delegate = nil; self.popover = nil; } - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { if ([self.popover isPopoverVisible]){ [self.popover dismissPopoverAnimated:NO]; NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(releasePopover) object:nil]; [[NSOperationQueue mainQueue] addOperation:invocationOperation]; } } - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController { [self releasePopover]; } 

Having said all this, if there is no good reason, you may just want the popover to be reused and only set it to zero when you receive low memory warnings and / or if your view is unloaded, as Chris Loonam replied:

+16
source

Try to use it in viewDidUnload if you really consider it necessary to do this. Since ARC automatically issues objects, I'm not sure if this is really necessary.

+1
source

Standing on Simon’s shoulders, answer, here is my correction for the accident:

 // set to nil on main queue to prevent "dealloc'd while still visible" exception dispatch_async(dispatch_get_main_queue(), ^{ self.popover = nil; }); 
+1
source

All Articles