我花了整整一天的时间追逐来自某个地方的内存泄漏......而我才发现它。
除此之外,我不知道它为什么会破碎。而且我不知道为什么我的改变已经解决了。
因此,我创建了一个最小的破坏性示例,以便在此处尝试清除为什么会发生这种情况。
故事板非常简单。它包含一个UINavigationController
,rootViewController
类型为ViewController
,而且会推送到AnotherViewController
。
我有一个协议定义......
protocol MyProtocol {}
AnotherViewController
符合它......
class AnotherViewcontroller: UIViewController, MyProtocol {
deinit {
print(#function, "Another View Controller")
}
}
我添加了deinit
方法来显示正在发生的事情。
ViewController
代码更有趣,但同样简单......
class ViewController: UIViewController, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.delegate = self
}
func navigationController(_ navigationController: UINavigationController,
animationControllerFor operation: UINavigationControllerOperation,
from fromVC: UIViewController,
to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
switch (fromVC, toVC) {
case (is MyProtocol, is MyProtocol):
print(" NOOOOOO!")
return nil
default:
print(" Yippee!")
return nil
}
}
}
ViewController
不符合MyProtocol
。
它成为navigationController
的代表。
并实现返回动画转换控制器的函数。但在这种情况下,我只是return nil
因为我不想改变动画。
这里没有什么复杂的事情发生。
当我运行应用程序时,它会打开,并在屏幕中央显示Push
按钮。我点按Push
按钮,转到AnotherViewController
。在控制台中我有 Yippee!
。
当我现在点击后退按钮时,我再次在控制台中获得 Yippee!
并弹出回第一个视图控制器。
注意:它永远不会显示红苹果。它永远不会进入那种情况。它所做的就是检查
fromVC
和toVC
是否符合MyProtocol
。
除此之外,AnotherViewController
尚未解除分配。 内存泄漏。如果我运行内存图调试器,我可以看到它仍在内存中。
所以,我找到了两种可能的方法来解决这个问题......做其中任何一个都会解决它。
第一种方式......将class
添加到协议中。通过将协议定义更改为protocol MyProtocol: class {}
,它可以解决问题。它仍会显示青苹果,但现在它将正常释放。
第二种方式......不要问视图控制器是否符合协议。如果我从交换机中删除case (is MyProtocol, is MyProtocol)
并保留默认值,那么它现在将正确释放视图控制器并修复内存泄漏。它不会改变行为。它仍然显示青苹果。但现在有效。
如果我单独留下所有代码并使ViewController
也符合MyProtocol
,那么AnotherViewController
将被正常释放(除非它现在显示红苹果)。
所以...为什么会这样?它对类协议位有意义,因为它现在知道它是一个引用类型......但仍然不确定为什么询问它是否符合导致泄漏?
还有......为什么要问它是否符合非类协议导致它泄漏?
如果删除switch
并仅使用if
检查它们是否符合协议if toVC is MyProtocol
,那么它仍然可以解除分配。它似乎只是在交换机中。
如果我删除了对协议的检查,而是检查了班级... case (is AnotherViewController, is AnotherViewController):
,那么这也不会影响它。它仍然正常解除分配。
如果有人能说清楚这一点,我很想知道这里发生了什么。
由于