问题
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