【问题标题】:Swift weak reference going nil even when self keeps existing即使 self 保持存在,Swift 弱引用也会为零
【发布时间】:2018-01-12 15:13:40
【问题描述】:

我试图弄清楚为什么即使在对象保持存在之后,弱引用也会丢失其引用的对象。

我的代码如下:

MyClass {
    deinit {
        print("I'm being deinited") // This never gets called
    }
    func doConnection(connection: Future<Data, ServerConnectionError>) {
        Future<Void, ServerConnectionError> { complete in
            connection.onSuccess {[weak self] data in
                guard let strongSelf = self else {
                    return // This line gets called
                }
                ...
            }
        }
    }
}

通过查看内存图,我可以看到在future完成之前被self引用的对象还活着(根据内存地址判断)。

在发现它的弱引用为 nil(最右边的 MyClass 实例)后,假设丢失对象的内存图如下所示:

上面的子树是用来保存对象的,而下面的子树与当前的堆栈执行有关。在第 3 层(从右到左)被许多其他对象引用的对象(蓝色框)持有对数组的强引用,而数组又持有对 MyClass 实例的引用。

编辑:在下面的答案中解决了问题。冷却结束后将标记为已解决。

【问题讨论】:

    标签: swift weak-references


    【解决方案1】:

    问题解决了。好的,所以我不知道发生了什么,但在多次尝试调试后它自行修复了。仍然有问题的一件事是调试器在闭包内将self 显示为nil(这在调试过程中增加了相当长的时间),但是guard let 块成功执行,我得到了对该对象的强引用.

    【讨论】:

      【解决方案2】:

      默认情况下,闭包表达式通过对这些值的强引用从其周围范围捕获属性。但是您将自我定义为在您的关闭/承诺中很弱。您可以删除 [weak self] 使用 [unowned self] 或将关闭列表移动到外部块。只要您的 MyClass 实例在履行承诺时没有被释放。只有当内存压力触发垃圾收集时,才会释放没有强引用的对象。但是,在 ARC 中,值会在其最后一个强引用被删除后立即释放。通过在闭包中设置 self weak ,您允许 ARC 在不存在其他强引用时删除对 MyClass 实例的引用,在取消初始化 MyClass 实例后,您的弱引用将被删除。

      【讨论】:

      • 我知道 weak 意味着对象可以被释放,我对此很好。如果引用丢失,我不想执行我的相关代码。问题是该对象仍在内存中,一直被强引用。它的 deinit 方法永远不会被调用,因此它永远不会丢失所有的强引用。
      【解决方案3】:

      将任何包含self 的捕获列表移动到最外面的闭包,否则它可能会创建一个引用循环:

      MyClass {
          deinit {
              print("I'm being deinited") // This never gets called
          }
          doConnection(connection: Future<Data, ServerConnectionError>) { [weak self] in
              Future<Void, ServerConnectionError> { complete in
                  connection.onSuccess { data in
                      guard let strongSelf = self else {
                          return // This line gets called
                      }
                      ...
                  }
              }
          }
      }
      

      【讨论】:

      • 我无法在此处放置捕获列表,因为它是一个类方法(抱歉,忘记添加 func)。已经尝试将捕获列表也放在Future&lt;Void... 行中,但没有成功。
      • 当你删除这行代码时,它每次都会取消初始化?我们确定这是罪魁祸首?
      • 请看我的回答。我猜这都是编译器的问题,因为它在几次尝试后自行修复。此外,调试器向我显示了错误的信息。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多