Swift Delegate 模式使用 Weak 避免循环引用
最近做项目遇到一个 Bug ,VC 本该销毁的时候仍然没有被销毁,引起内存泄露。反复找问题后定位到其中一个 View
的 Delegate
没有设置为 weak(弱引用)
导致。现在就详细分析一下原因和解决办法。
ARC 自动引用计数
首先得从自动引计数讲起,Swift 使用自动引用计数 (ARC) 来跟踪并管理应用使用的内存。大部分情况下,这意味着在 Swift 语言中,内存管理自动完成,不需要自己去考虑内存管理的事情。当实例不再被使用时,ARC会自动释放这些类的实例所占用的内存。
每一个实例有引用数,每次有别的实例引用它时,引用数加一。每一次引用该实例的实例销毁时,引用数减一。等引用数等于0时,该实例被销毁。
更多详情看这里 Swift 自动引用计数。
循环引用
当我们创建一个 VC 并 VC 含有一个 View 实例如下
1 | class mainController() { |
上述代码,当运行 test
函数时,创建一个 vc
实例,该实例的引用计数为 1,执行完此函数后 vc
的引用计数变为0,因为没有地方引用了。就会销毁,当 vc
被销毁后,view
的引用数变为 0,也正常销毁。这个是正常销毁的过程。
但是如果上述代码中做这些修改:
1 | ... |
则会引起循环引用,因为 vc
引用了 view
,view
引用了 vc
。两者的引用数永远不会为0。该实例不会被销毁,引起循环引用。
使用 weak
引起循环引用的根本原因就是相互引用导致引用数各自加 1,解决办法则是把 强引用( 增加引用数 )
改为 弱引用( 不增加引用数 )
。根据实际情况,一般把 被动
及 子实例
设为对 父实例
弱引用。
设置 Delegate 的基本原则:所有的 Delegate 应该设为 weak
1 | protocol SomeProtocolOne { |
由于 Swift 中任何元素都可以遵循协议,但是非 class 类不能使用 weak。 所以需要设置为 delegate 的协议需要遵循 class
协议。来声明自己是 class-only-protocol 。否则没法设置 weak
。