本文最后更新于 2021年4月4日 晚上
在程序中构造了一个使用 window 来展示的弹窗类, 弹窗类内部持有一个 window, 然后通过 window 持有一个根视图控制器, 通过在根视图控制器上添加对应的显示内容, 从而显示弹窗. 但就是这样一个简单的结构, 却引起了循环引用, 下面就来进行分析.
上下文
原来的实现如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| typealias VoidCallBack = () -> () typealias DataCallBack<T> = (T) -> Void
class AlertWindowView { private var _window: UIWindow? private weak var _rootVC: UIViewController? func show(content: UIView, onTapBG: VoidCallBack? = nil, initialConstratins: DataCallBack<(UIView, UIView)>?, normalConstraints: DataCallBack<(UIView, UIView)>?) { vc.contentViewInitialConstaints = initialConstratins vc.contentViewNormalConstaints = normalConstraints }
func showContentWithCommonLayoutConstaints(content: UIViewController & AlertAddible, onTapBG: VoidCallBack? = nil) { show(content: content, initialConstratins: commonInitialConstraints, normalConstraints: commonNormalConstraints) } private func commonInitialConstraints(contentAndSuperView: (UIView, UIView)) { private func commonNormalConstraints(contentAndSuperView: (UIView, UIView)) { }
private class AlertWindowViewController: UIViewController { var contentViewInitialConstaints: DataCallBack<(UIView, UIView)>? var contentViewNormalConstaints: DataCallBack<(UIView, UIView)>? }
|
定位到在赋值 AlertWindowViewController 的 contentViewInitialConstaints 和 contentViewNormalConstaints 两个闭包属性时, 传入的时候传入的是 AlertWindowView 的是 commonInitialConstraints 和 commonNormalConstraints 两个方法作为实参, 从而造成循环引用.
因为 AlertWindowView 持有 window, window 持有 AlertWindowViewController 对象, 而 AlertWindowViewController 对象中通过 contentViewInitialConstaints 或 contentViewNormalConstaints 持有 AlertWindowView 的 commonInitialConstraints 方法, 这样会隐式地持有 AlertWindowView 对象, 从而造成循环引用.
教训
在某个对象 A 中, 持有对象 B, 而对象 B 中有闭包存储属性, 闭包从 A 中传过去的时候, 一定要注意不要直接传 A 中的方法作为闭包参数, 因为这样会造成在 B 中隐式地持有 A, 从而造成循环引用.
故上面的代码中修改如下即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| func showContentWithCommonLayoutConstaints(content: UIViewController & AlertAddible, onTapBG: VoidCallBack? = nil) {
show(content: content, initialConstratins: { [weak self] in self?.commonInitialConstraints(contentAndSuperView: $0) }, normalConstraints: { [weak self] in self?.commonNormalConstraints(contentAndSuperView: $0) }) }
|