在程序中构造了一个使用 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) }) }
|