Object deallocated in ARC mode

2019-06-05 06:12发布

问题:

The object is deallocated in ARC mode and causes crash. My code below;

BlockAlertView* alert = [BlockAlertView alertWithTitle:title message:message];
[alert setCancelButtonWithTitle:NSLocalizedString(@"No", @"No Button") block:nil];
[alert addButtonWithTitle:NSLocalizedString(@"Yes", @"Yes Button") block:^{
        //Do Something
}];
[alert show];

It appears the correct alert view(which is custom UIView) but when I click one of the button it crashes.

The crash log;

2015-04-07 22:28:17.065 Test[3213:781570] <BlockAlertView: 0x16bb4160> 2015-04-07 22:33:01.746 Test[3213:781570] *** -[BlockAlertView performSelector:withObject:withObject:]: message sent to deallocated instance 0x16bb4160

Here is the source code of BlockAlertView;

BlockAlertView on Github

For now I can't estimate any of the clues for this and makes me old. Any input will be much appreciated!

回答1:

Presumably, that code worked correctly before it was converted to ARC.

To fix it, you'll need to create a strong reference to self in the -show method, and release this reference in -dismissWithClickedButtonIndex:animated: (where you see [self autorelease] commented out).

You can do this with a simple instance variable:

id _selfReference;

Assign self to _selfReference in -show:

- (void)show
{
    _selfReference = self;
    ...

and then set _selfReference to nil in -dismissWithClickedButtonIndex:animated: in the two places where you see [self autorelease] commented out.



回答2:

Assign your alert object to live somewhere beyond your current function. One simple possibility is to just make it an instance variable. If that's not practical, create an instance NSMutableArray *retainedItems; which you allocate/init, and stuff this into.



回答3:

Looks like a design flaw in that project. The class is poorly named BlockAlertView as it's not actually a subclass of UIView. If it were a view and it was added to the view hierarchy then the view hierarchy would ensure it stayed alive whilst being viewed. As it is the view is kept alive but the object that created the view BlockAlertView is not held onto by anything and by the time the actions are called BlockAlertView is long gone.

This will require you to keep a strong ivar around to reference this "controller" object and it would be sensible to nil out that ivar in the completion blocks.

BlockAlertView *alertController = [BlockAlertView alertWithTitle:title message:message]; {
  [alertController setCancelButtonWithTitle:NSLocalizedString(@"No", @"No Button") block:nil];

  __weak __typeof(self) weakSelf = self;
  [alertController addButtonWithTitle:NSLocalizedString(@"Yes", @"Yes Button") block:^{
    //Do Something
    weakSelf.alertController = nil;
  }];

  [alertController show];
}

self.alertController = alertController;