NSTimer 循環引用造成的 retain cycle
NSTimer 的 retain cycle 的問題
有時我們常常需要在 Controller 中建立一個定時被呼叫的函數,所以會使用到 NSTimer,NSTimer 很容易產生 retain cycle 的狀況,下列一個是很常見的例子
1 | class ViewController: UIViewController { |
發生了什麼
由於 timer
建立時引用了 ViewController
,而 timer
本身又是 ViewController
的成員變數,所以這樣就造成了 retain cycle
,就算我們在 deinit
解構的時候讓 timer invalidate
跟讓 timer assign
成 nil
,但是由於已經產生了 retain cycle
,所以 ViewController
的 deinit
完全不會被呼叫到。
解決方法?
或許我們可以先想到,如果我們使用 weak self
給 target
呢 ? 答案依舊相同,可以參考一下官方文檔是這樣寫的:
The timer maintains a strong reference to
target
until it (the timer) is invalidated.
timer
被建立時的引用永遠是 strong
的,所以這個方法行不通。
那如果我們將 timer
設成 weak
屬性呢? 一樣也是行不通的,參考官方文檔:
Timers work in conjunction with run loops. Run loops maintain strong references to their timers, so you don’t have to maintain your own strong reference to a timer after you have added it to a run loop.
雖然 timer
不會被 ViewController
引用了,但是 self target
的問題一樣沒有被解決,然後 timer
也會被加到 run loop
中,所以依舊不會被釋放。
iOS 10 Closure 的解決方法
在 iOS10 中,提供了有 Closure
的建立方法 :
1 | class func scheduledTimer(withTimeInterval interval: TimeInterval, |
所以利用這種方法,我們可以改寫成
1 | class ViewController: UIViewController { |
使用 Closure
的寫法,就可以避免 retain cycle
了,那如果不是 iOS10呢?
可以加入一個中介層(proxy),或者自己實做跟 iOS10 api 相同的事情,可以參照這幾個連結的解法 :
https://gist.github.com/onevcat/2d1ceff1c657591eebde
http://www.jianshu.com/p/4cfae008bff5
參考資料:
http://nelson.logdown.com/posts/2017/04/05/how-to-fix-nstimer-retain-cycle