讨论weak self不能解决的循环引用
一般情况ViewController的deinit 比如在主ViewController里push进VC2
1 2 3 4 5 6 7 8 9 10 11 12 class ViewController : UIViewController { override func viewDidLoad () { super .viewDidLoad() } @IBAction func pushBtn (_ sender : Any ) { let vc = VC2 () navigationController? .pushViewController(vc, animated: true ) } }
VC2里简单看下deninit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class VC2 : UIViewController { override func viewDidLoad () { super .viewDidLoad() view.backgroundColor = .systemCyan } override func viewWillLayoutSubviews () { super .viewWillLayoutSubviews() } deinit { print ("VC2 deinit" ) } }
当从VC2返回navigationController时可以看到”VC2 deinit”打印了
加入CADisplayLink 在VC2里加入CADisplayLink,我们知道CADisplayLink跟着屏幕刷新率走,可以搞一些Core Animation事情
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 class VC2 : UIViewController { var displayLink: CADisplayLink ? override func viewDidLoad () { super .viewDidLoad() view.backgroundColor = .systemCyan } override func viewWillLayoutSubviews () { super .viewWillLayoutSubviews() displayLink = CADisplayLink (target: self , selector: #selector(displayLinkSel)) displayLink? .frameInterval = 1 displayLink? .add(to: RunLoop .current, forMode: .default) } @objc func displayLinkSel () { print ("displayLinkSel" ) } deinit { print ("VC2 deinit" ) } }
这里运行看到每一frame刷新都打印了”displayLinkSel”,但是退出时候”VC2 deinit”没有打印,而且”displayLinkSel”一直继续打印,说明循环引用了 你可能会想用弱引用解决
1 2 3 4 5 6 7 8 9 10 11 12 override func viewWillLayoutSubviews () { super .viewWillLayoutSubviews() weak var weakSelf = self displayLink = CADisplayLink (target: weakSelf, selector: #selector(displayLinkSel)) displayLink? .frameInterval = 1 displayLink? .add(to: RunLoop .current, forMode: .default) } deinit { displayLink = nil print ("VC2 deinit" ) }
结果还是一样,没有deinit触发,想验证的话可以lldb看下vc2
解决 写一个代理,这里代理直接让他走消息转发机制,类似objc_mesgSend,我们知道iOS里回去class对象和meta-class对象的方法区找,直接转发就是当objc_mesgSend找不到fail掉的最后最后一步,这样提高效率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class MyWeakProxy : NSObject { weak var target: NSObjectProtocol ? init (target : NSObjectProtocol ) { self .target = target super .init () } override func responds (to aSelector : Selector !) -> Bool { return (target? .responds(to: aSelector) ?? false ) || super .responds(to: aSelector) } override func forwardingTarget (for aSelector : Selector !) -> Any ? { return target } }
然后,我们让target是刚刚写的代理类
1 2 3 4 5 6 7 8 9 10 11 override func viewWillLayoutSubviews () { super .viewWillLayoutSubviews() displayLink = CADisplayLink (target: MyWeakProxy (target: self ), selector: #selector(displayLinkSel)) displayLink? .frameInterval = 1 displayLink? .add(to: RunLoop .current, forMode: .default) } deinit { displayLink? .isPaused = true print ("VC2 deinit" ) }
最后解释下为什么weak self不起作用,原因不复杂,就是传入时候strong引用了,相当于weakSelf!,这样设计也有道理,因为这玩意和runloop绑定的。
1 2 weak var weakSelf = self displayLink = CADisplayLink (target: weakSelf! , selector: #selector(displayLinkSel))