【问题标题】:Add observer to runloop creates retain cycle将观察者添加到运行循环创建保留循环
【发布时间】:2017-05-30 03:34:49
【问题描述】:
let observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, CFRunLoopActivity.BeforeWaiting.rawValue, false, 0, { (observer, activity) in

        self.doSomething()

    })

    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, UITrackingRunLoopMode)

我在 UIViewController 的主运行循环中添加了一个观察者,这意味着 self 是 UIViewController 的一个实例。上面的代码创建了一个保持循环,导致控制器永远不会被释放。

我知道我可以为块声明 [weak self] 或 [unowned self] 来解决问题。我想问一下retian循环到底是什么样的?我只知道该块通过强引用捕获自我。

【问题讨论】:

    标签: ios nsrunloop retain-cycle


    【解决方案1】:

    如果您在“Debug Memory Graph”中运行此程序并启用“Malloc Stack”功能(并使用“Malloc Scribble”来减少误报),您将看到观察者的闭包可能会保持对视图控制器的强引用:

    因为我们开启了“Malloc Stack”功能,你可以点击右侧的堆栈,它会直接带你进入建立强引用的代码。 (不用说,当你完成调试后,关闭“Malloc Stack”。)

    作为一般规则,当您有一个引用 self 的闭包时,您将需要使用 [weak self] 模式:

    let observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, CFRunLoopActivity.beforeWaiting.rawValue, false, 0) { [weak self] observer, activity in
        self?.doSomething()
    }
    

    这破坏了运行循环的观察者对视图控制器的强引用。然后,再次,如果您的观察者在视图控制器被关闭时仍然存在,您可能希望在视图控制器被关闭时记住CFRunLoopRemoveObserver

    所有这些都说了,如果你的观察者没有重复并且你没有保留对它的引用,通常这个闭包会被自己释放并且你对视图控制器的强引用会被自动解决。但这里的细节不如“调试内存图”工具将有助于找到强引用的一般观察重要,无论它们在哪里。

    有关更多信息,请参阅 WWDC 2016 Visual Debugging with Xcode,从该视频大约 24 分钟开始,将向您展示如何诊断此类问题。

    【讨论】:

      【解决方案2】:

      您问题的代码中没有保留周期。

      • 运行循环保留观察者。
      • 观察者保留块。
      • 该块保留self

      这不是保留循环。

      但是,如果您将observer 存储在strong 实例变量中,那么您就有一个保留周期:

      • self 保留观察者。
      • 观察者保留块。
      • 该块保留self

      【讨论】:

      • 我确定我没有将观察者存储在强实例变量中。如果我不将self声明为weak self进入block,它将永远不会被释放,
      • 你有没有从运行循环中移除观察者?如果你不从运行循环中移除观察者,那么观察者将永远不会被释放。如果观察者从未被释放,则该块将永远不会被释放。如果块永远不会被释放,则视图控制器将永远不会被释放。这并不意味着有一个保留周期!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-07
      • 1970-01-01
      • 2018-01-29
      • 1970-01-01
      • 2021-02-28
      • 2022-01-26
      相关资源
      最近更新 更多