【问题标题】:Why does UIAlertController create a retain cycle with self?为什么 UIAlertController 会用 self 创建一个保留循环?
【发布时间】:2017-11-02 00:55:16
【问题描述】:
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];

我了解周期self 保留UIAlertControllerUIAlertController 保留UIAlertActionUIAlertAction 保留self

我的意思是,这个类在内部不能设计为在运行 UIAlertAction 之一后释放所有内容吗?

-

为了澄清,我知道可以通过使用对 self 的弱引用来避免这个问题。

我要问的是,为什么 UIAlertController 在用户选择了一个动作后不直接清除所有动作(以及它们的处理程序块)。这将打破循环并避免我们需要做的整个弱者之舞。

这样的……

@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
}

【问题讨论】:

    标签: ios memory-management objective-c-blocks uialertcontroller retain-cycle


    【解决方案1】:

    这里的问题和答案让我感到困惑,这是我搜索结果的顶部,所以我想澄清一下:

    示例中没有保留循环,因此在这种情况下无需创建“弱自我”。唯一存在保留周期的情况是 self 在 alert 上有很强的参考。

    UIAlertController 被设计为在执行后释放所有内容,前提是您不持有对它的强引用。

    我在示例类中尝试了该示例,并且在控制器弹出时成功调用了dealloc。

    举例总结:

    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];
    

    //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];
    

    旁注:假设在将 self 放入块中时总是需要对 self 使用弱引用是错误的。

    【讨论】:

    • 曼尼你有一个文档链接或支持这一点的文章:“UIAlertController 被设计为在执行后释放所有内容,前提是你没有强烈引用它。 "
    • @alexisSchreier 从视图层次结构中删除视图(及其控制器)已成为标准做法(近 40 年)。此外,Manny 对其进行了测试,如果您想验证它,您可以轻松地测试自己。可能有一些 WWDC 会议或 30 年前的文档,但很难找到它。对 self 的强引用是默认设置,因为大多数时候代码需要 self 才能正常工作。只有在绝对必要时才应使用弱引用以避免保留循环。
    【解决方案2】:

    问题不在于 UIAlertController 是如何设计的,而是块如何工作,即它捕获/保留引用对象,除非引用变量被标记为弱。 我认为将块代码从[self doSomething]; 更改为[weakSelf doSomething]; 应该可以修复保留周期。 weakSelf 变量可以在动作创建之前声明,如下所示:

    __weak YourViewCOntrollerClass *weakSelf = self;

    【讨论】:

    • 我知道如何通过弱引用来防止循环。我在问为什么 UIAlertController 在完成后不只是清除块。
    【解决方案3】:

    实际上,当我们在块中使用强实例时(就像您使用 self 一样),它创建单独的副本会增加保留计数。在该类通过调用 dealloc 方法减少保留计数之后。但不能使其为零。 弱引用在使用后释放计数。所以,像这样创建弱引用:

       __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];
    

    【讨论】:

    • 我知道如何通过弱引用来防止循环。我在问为什么 UIAlertController 在完成后不只是清除块。
    • 我也说过,强变量在块中创建自己的单独副本。不会释放内存。因此,请始终使用弱引用。
    • 是的,但是一旦块运行,它就可以被释放,就像调度队列一样。 (您可以在调度队列块中安全地使用 self)
    猜你喜欢
    • 1970-01-01
    • 2021-05-14
    • 2018-02-22
    • 2011-05-20
    • 1970-01-01
    • 1970-01-01
    • 2018-05-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多