【问题标题】:Breaking retain cycle with strong/weak self用强/弱自我打破保留周期
【发布时间】:2015-09-25 18:17:15
【问题描述】:

我读过关于强/弱self 以打破保留周期的帖子,但我仍然对它们的工作方式感到困惑。我了解使用 __weak typeof(self) weakSelf = self 创建对 self 的弱引用,但我对强引用感到困惑。据我了解,强引用是对self 的强引用,这样它就不会在块结束之前被释放,对吗?那么为什么需要__strong typeof(self) strongSelf = weakSelf呢?这最终不会指向 self 对象吗?那么为什么不直接strongSelf = self呢?

【问题讨论】:

    标签: ios objective-c cocoa automatic-ref-counting


    【解决方案1】:

    您在块内引用的任何非弱对象都将导致对该对象的隐式保留,因为正在创建块。未执行,但已创建。

    如果你直接从 self 初始化内部 strongSelf,你会保留 self 的值,并可能导致一个保留循环。

    另一方面,如果你从weakSelf初始化它,你将不会保留weakSelf的值。

    这就是两步的原因。外层代码将self的值复制到weakSelf中,但是ARC没有添加retain,因为它是__weak()。

    块“创建”复制了weakSelf的值(或者至少,设法使其值在执行时可用)。你看不到它把它复制到了哪里,但它确实复制了。

    在块“执行”时,块将“weakSelf 的值”(如果同时释放 self 则为零)复制到 strongSelf,然后 ARC 对其应用保留。因此,在块的持续时间内,strongSelf 引用的对象将保持活动状态,如果它一开始是活动的。如果你只依赖weakSelf,它可能会在块执行期间的任何时候变为nil。

    请注意,weak/strong 模式是带大括号的 - 许多示例实际上依赖于这样一个事实:weakSelf 将变为 nil,并且该块将默默地变成 no-ops 的集合(消息为 nil)。

    保留循环通常仅在以下情况下发生:(a)您在 self.property 中保留对块的引用,或(b)您将块交给其他对象(通知管理器等),并告诉其他对象在你的dealloc中忘记它;在这两种情况下,当块处于活动状态时,您的 dealloc 将永远不会被调用。

    当人们说“做这件事的方法是使用弱/强模式”时,他们是在假设最坏的情况。

    【讨论】:

    • 哦,所以weakSelf = self 分配了weakSelf self 的副本?这似乎有点奇怪,我认为它指向原始的self。是因为阻塞还是变量赋值的工作原理?
    • @user1813076 - 不,weakSelf = self 确实weakSelf 分配给self 的副本。 weakSelf 是一个指向self 的指针,当self 被释放时它设置为nil
    • 闭包和变量作用域的鬼鬼祟祟的本质无助于这一点。本质上是复制的实际变量“weakSelf”,而不是它指向的对象。
    • 认为weakSelf、strongSelf 和self 都是指向 单个对象的变量。当您将该指针复制到另一个变量中时,ARC 会介入并“增加引用对象的保留计数”,除非该变量是 __weak(),在这种情况下,如果引用的对象已解除分配,它将安排该变量为空.块秘密地复制它们引用的所有外部范围变量。不是对象,只是变量。 (注意:这里忽略了__block,这又是一个不同的游戏)
    • 在 NotificationCenter 调用块中使用实例时,为什么我们需要使用 _weak?它不会创建任何保留周期吗?
    【解决方案2】:

    模式是:

    __weak typeof(self) weakSelf = self;
    
    [manager someAsynchronousMethodWithCompletionHandler:^{
        typeof(self) strongSelf = weakSelf;
        if (strongSelf) {
            ...
        }
    }];
    

    这个想法是完成处理程序块将只有一个对self的弱引用,因此如果在调用完成块之前释放self,那么它将被安全地释放,因为该块没有强引用给它。 (一个常见的例子是当一个视图控制器发起一些异步网络请求来更新一个视图时,如果视图控制器在网络请求完成之前被关闭,那么对于一个早已消失的视图来说,挂在视图控制器实例上是没有意义的。)

    但是,这个weakSelf/strongSelf 模式还确保如果发生相反的情况,完成块已经开始并且在self 被释放之前遇到strongSelf 行,该块将确保self在该块的运行期间被保留(即它不能在完成块的运行中途被释放,即使它在不同的线程上运行)。这有许多潜在的好处(从对象完整性到消除竞争条件)。有时您实际上并不需要“weakSelf/strongSelf dance”的一半strongSelf,但在需要时它是一个非常宝贵的工具。

    但是,如果您在块内有一行写着 typeof(self) strongSelf = self(而不是 weakSelf),则该语句右侧仅存在 self 将导致块保持强在前面引用self,完全违背了使用weakSelf的目的。

    【讨论】:

    • 我认为你的模式不对。 strongSelf from self 的内部赋值应该来自weakself。
    • 该块内应该是typeof(self) strongSelf = weakSelf;
    • 但是strongSelf = weakSelf 不是让strongSelf 指向self 吗?还是发生了一些稍微不同的事情
    • @user1813076 如果self 尚未发布,strongSelf = weakSelf strongSelf 设置为self。但是如果self 在它到达那行代码时已经被释放,那么weakSelf 将是nil,因此strongSelf 也将被设置为nil
    • @user1813076 - 不,它不一样,这就是为什么你没有强大的参考周期。如果一个块直接引用self,则该块在实例化后立即建立对self 的强引用(这很容易导致强引用循环)。但是如果你引用weakSelf,块的存在并不会建立对self的强引用。如果你有一行写着strongSelf = weakSelf,那不会在块被实例化时建立强引用,而是只有在该行代码运行时。因此没有强参考周期。
    【解决方案3】:

    有正确的答案,但我真的不知道它们是否是您 Q 的答案。它们通常使用 self 解释了带有块的保留循环问题,但您的 Q 是:

    那么为什么不只是 strongSelf = self?

    这个问题的答案:

    如果有人这样做,self 将成为块关闭的一部分并始终保留。自我虚弱的整个故事将毫无意义。

    【讨论】:

      【解决方案4】:

      简单来说

      __weak typeof(self) weakSelf = self - 我们在块之前使用它,

      这只是指向自身的指针,没有开始保留循环

      -- 当且仅当块将执行时才会完成保留

      --> 如果我们让它变得强大,那么它会开始保留循环并消耗内存,即使我们不调用块

      【讨论】:

        【解决方案5】:

        必须清除一点,您没有使用弱/强模式以避免保留循环!无论您使用对 self 或 weakself 的强引用,您都没有避免保留循环。引用变量范围破坏了保留循环!当它到达作用域的末尾时,引用变量被释放。弱/强模式只是一种保护机制,可防止您对 nil 进行强引用,因为在创建和运行块之前,self 可能会被释放。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-01-02
          • 2012-04-04
          • 2013-04-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多