【问题标题】:Swift retain cycle explanationSwift 保留循环解释
【发布时间】:2016-08-14 05:37:40
【问题描述】:

这是我的自定义视图:

class CustomVIew: UIView {

    deinit {
        print("custom view deinit")
    }

    var onTapViewHandler: (()->Void)?
}

和视图控制器:

class ViewControllerB: UIViewController {

    var customView: CustomVIew!

    deinit {
        print("B deinit")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let customView = CustomVIew()
        customView.onTapViewHandler = { [unowned self] in
            self.didTapBlue()
        }
        customView.frame = CGRect(x: 50, y: 250, width: 200, height: 100)
        customView.backgroundColor = UIColor.blueColor()
        view.addSubview(customView)

        self.customView = customView

    }

    func didTapBlue() {

    }
}

当控制器从导航栈中弹出时,一切正常:

B deinit
custom view deinit

但是当我替换这段代码时:

customView.onTapViewHandler = { [unowned self] in
     self.didTapBlue()
}

用这个:

 customView.onTapViewHandler = didTapBlue

然后,控制台上不会打印任何内容。 CustomView 和 ViewController 没有发布,为什么?

为什么customView.onTapViewHandler = didTapBlue 会捕获对self 的引用?

【问题讨论】:

  • 你知道为什么已经不是了吗?你的标题说它是保留圈...通过打印检查引用计数: print(CFGetRetainCount(object))
  • @当然你建议CFGetRetainCount 解决retain 被弃用的问题。但是retain 被弃用是有原因的:这不是一个好的解决方案。
  • 这只是一种检查方法。我的意思是您可以打印出视图控制器被引用的引用量,以便您知道“customView”是否对视图控制器具有强引用。如果有,则有一个保留周期。因为它有一个retain循环,所以这两个对象不能被自动销毁。
  • @Surely 永远不要检查CFGetRetainCount。你得到的信息没有用。例如,CFGetRetainCount 导入 Swift 的方式使其所有结果都增加 1。永远不要检查保留计数。甚至文档都这么说。
  • @Sulthan 数量不重要,重要的是数量是否增加。我用它来计算引用,它有助于确定一行代码是否增加了对象的引用计数,因此我知道它是否会导致问题。而且这段代码只是为了调试,我并不是说你发布应用程序时会保留这段代码。不计参考,我很好奇你如何调试这样的问题?

标签: ios iphone swift swift2


【解决方案1】:

Swift 函数是一种闭包。所以像闭包(目标c中的块)一样,函数可以捕获引用。

customView.onTapViewHandler = didTapBlue 执行对self 的引用时,即ViewControllerB 在这种情况下的引用将被函数调用捕获。

同时ViewControllerB 的视图对CustomVIew 有很强的引用,因此它会产生保留周期。

关于使用unownedApple document 说:

弱引用和无主引用在引用周期中启用一个实例 引用另一个实例而不牢牢抓住它。 然后实例可以相互引用而不创建强 参考循环。

这意味着没有循环引用和保留循环。

【讨论】:

  • 谢谢。我只是不明白为什么customView.onTapViewHandler = didTapBlue 执行会捕获self' 的引用,你能告诉我吗?
  • didTapBlueself.didTapBlue。所以customView 应该持有对didTapBlue 的引用,只有这样它才能执行该函数。如您所知,没有对象的引用,didTapBlue 就无法生存。所以闭包、块和方法添加了对实例的强引用
【解决方案2】:

如果将[unowned self] 捕获列表添加到闭包中,则视图持有对self 的弱引用,self 持有对视图的强引用。

由于没有对self 的强引用,因此可以在弹出视图控制器时取消初始化self。在 self 被取消初始化后,没有任何东西对视图有强引用,所以 it 也被取消初始化。

如果您删除捕获列表,self 持有对视图的强引用,而视图持有对 self 的强引用。这意味着为了取消初始化self,必须首先取消初始化视图(这将破坏强引用)。但是为了取消初始化视图,必须首先取消初始化视图控制器以破坏对视图的强引用。但是除非取消初始化视图控制器,否则不能破坏对视图的强引用。除非取消初始化视图,否则不能破坏对视图控制器的强引用。

看到了吗?我们在这里陷入了无限循环!所以视图和视图控制器都不会被取消初始化!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-14
    相关资源
    最近更新 更多