检查控制器是否被销毁

检查内存泄漏除了使用Instruments,还有查看控制器pop或dismiss后是否被销毁,后者相对来说更方便一点.但老是盯着析构函数deinit看日志输出是否有点麻烦呢?

UIViewController有提供两个不知名的属性:

isBeingDismissed: 当modal出来的控制器被dismiss后的值为true.
isMovingFromParent: 在控制器的堆栈中,如果当前控制器从父控制器中移除,值会变成true.
如果这两个属性都为true,表明控制器马上要被销毁了,但这是由ARC去做内存管理,我们并不知道多久之后被销毁,简单起见就设个2秒吧.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extension UIViewController {

public func dch_checkDeallocation(afterDelay delay: TimeInterval = 2.0) {
let rootParentViewController = dch_rootParentViewController

if isMovingFromParent || rootParentViewController.isBeingDismissed {
let disappearanceSource: String = isMovingFromParent ? "removed from its parent" : "dismissed"
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: { [weak self] in
if let VC = self {
assert(self == nil, "\(VC.description) not deallocated after being \(disappearanceSource)")
}
})
}
}
private var dch_rootParentViewController: UIViewController {
var root = self
while let parent = root.parent {
root = parent
}
return root
}
}

我们把这个方法添加到viewDidDisappear(_:)中

1
2
3
4
5
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)

dch_checkDeallocation()
}

如果发生循环引用,控制就不会被销毁,会触发assert报错.