Why does UIAlertController create a retain cycle with self?

耗尽温柔 提交于 2019-12-10 15:36:57

问题


UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"alert" message:nil preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action = [UIAlertAction actionWithTitle:@"action" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    [self doSomething];
}];
[alert addAction:action];
[self presentViewController:alert animated:YES completion:nil];

I understand the cycle. self retains the UIAlertController, UIAlertController retains the UIAlertAction, UIAlertAction retains self

What I mean is internally couldn't this class have been designed to release everything after one of the UIAlertActions has been run?

-

To clarify, I know that this issue can be avoided by using a weak reference to self.

What I am asking is why doesn't UIAlertController just nil out all the actions (and hence their handler blocks) once an action has been selected by the user. This would break the cycle and avoid the whole weakself dance we need to do.

Something like this...

@implementation UIAlertController

...

// An action button was pressed
- (void)actionSelectedIndex:(NSInteger)index
{
    UIAlertAction *action = self.actions[index];
    action.handler(action); // Run the action handler block
    self.actions = nil; // Release all the actions
}

回答1:


The question and answers here baffled me, and this is on top of my search results so I'd like to set the record straight:

There is no retain cycle in the example, so there is no need to create a "weak self" in this case. The only time that there's a retain cycle is if self has a strong reference on alert.

UIAlertController has been designed to release everything after it has been executed, provided you don't hold a strong reference to it.

I tried the example in a sample class, and dealloc was successfully called on pop of the controller.

To summarise by example:

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"alert" message:nil preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action = [UIAlertAction actionWithTitle:@"action" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    //No retain cycle, no need for weak self
    [self doSomething];
}];
[alert addAction:action];
[self presentViewController:alert animated:YES completion:nil];

vs

//assume this is a strong reference
self.alert = [UIAlertController alertControllerWithTitle:@"alert" message:nil preferredStyle:UIAlertControllerStyleAlert];
__weak __typeof(self)weakSelf = self;
UIAlertAction *action = [UIAlertAction actionWithTitle:@"action" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    //Need a weakSelf to avoid retain cycle
    [weakSelf doSomething];
}];
[self.alert addAction:action];
[self presentViewController:self.alert animated:YES completion:nil];

Side note: It's wrong to assume that you always need to use weak reference to self when putting it in blocks.




回答2:


The issue is not how UIAlertController is designed, but how block works i.e it captures/retains the reference object unless the reference variable is marked weak. I think changing the block code from [self doSomething]; to [weakSelf doSomething]; should fix the retain cycle. Where the weakSelf variable can be declared before the action creation like below:

__weak YourViewCOntrollerClass *weakSelf = self;




回答3:


Actually when we use strong instance in block( like you are using self ), it create the separate copy increase the retain count. After that class decrement the retain count by calling dealloc method. But cannot make it zero. weak reference release the count after its use. so, create weak reference like this:

   __weak __typeof(self)weakSelf = self;

 UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"alert" message:nil preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *action = [UIAlertAction actionWithTitle:@"action" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        [weakSelf doSomething];
    }];
    [alert addAction:action];
    [self presentViewController:alert animated:YES completion:nil];


来源:https://stackoverflow.com/questions/44297982/why-does-uialertcontroller-create-a-retain-cycle-with-self

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!