Swfit 对象方法作为实参造成循环引用的处理

本文最后更新于 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)>?
// ...
}

定位到在赋值 AlertWindowViewControllercontentViewInitialConstaintscontentViewNormalConstaints 两个闭包属性时, 传入的时候传入的是 AlertWindowView 的是 commonInitialConstraintscommonNormalConstraints 两个方法作为实参, 从而造成循环引用.

因为 AlertWindowView 持有 window, window 持有 AlertWindowViewController 对象, 而 AlertWindowViewController 对象中通过 contentViewInitialConstaintscontentViewNormalConstaints 持有 AlertWindowViewcommonInitialConstraints 方法, 这样会隐式地持有 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: commonInitialConstraints,
// normalConstraints: commonNormalConstraints)

// 替换为:

show(content: content, initialConstratins: { [weak self] in
self?.commonInitialConstraints(contentAndSuperView: $0)
}, normalConstraints: { [weak self] in
self?.commonNormalConstraints(contentAndSuperView: $0)
})
}

Swfit 对象方法作为实参造成循环引用的处理
https://blog.rayy.top/2019/09/11/2019-2019-09-11-swiftCircleReference/
作者
貘鸣
发布于
2019年9月11日
许可协议