NSTimer / CADisplayLink循環(huán)引用問(wèn)題分析

背景:在使用定時(shí)器的時(shí)候,一不小心就會(huì)遇到循環(huán)引用問(wèn)題,導(dǎo)致控制器不會(huì)被銷(xiāo)毀,定時(shí)事件也不會(huì)被終止。

錯(cuò)誤代碼

class ViewController: UIViewController {
    var displayLink: CADisplayLink?
    // var timer: Timer?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(test), userInfo: nil, repeats: true)
        displayLink = CADisplayLink(target: self, selector: #selector(test))
        displayLink?.add(to: .current, forMode: .default)
    }
    
    @objc func test() {
        print("\(#function)")
    }

    deinit {
        print("\(#function)")
        displayLink?.invalidate()
        // timer?.invalidate();
    }
}

分析

循環(huán)引用

如圖,控制器Vc強(qiáng)引用displayLink對(duì)象,CADisplayLink對(duì)象內(nèi)部的target強(qiáng)引用著Vc,循環(huán)引用,誰(shuí)也別想銷(xiāo)毀,導(dǎo)致內(nèi)存泄漏。

解決辦法

target與控制器之間弱引用

因CADisplayLink是系統(tǒng)的類(lèi),無(wú)法改變target的引用方式,所以可以新建一個(gè)中間類(lèi),中轉(zhuǎn)target,解決循環(huán)引用問(wèn)題。

控制器將要被釋放的時(shí)候,發(fā)現(xiàn)沒(méi)有被強(qiáng)引用,控制器被銷(xiāo)毀,同時(shí)定時(shí)器也被銷(xiāo)毀,Proxy也被銷(xiāo)毀,沒(méi)有內(nèi)存泄漏。

解決循環(huán)引用問(wèn)題

Proxy示例代碼如下

class Proxy: NSObject {
    // 弱指針
    weak var target: NSObject?
    
    class func with(target: NSObject) -> Proxy {
        let proxy = Proxy()
        proxy.target = target
        return proxy;
    }
    
    /// 重點(diǎn):消息轉(zhuǎn)發(fā)機(jī)制
    override func forwardingTarget(for aSelector: Selector!) -> Any? {
        return target
    }
}

Controller變更代碼如下

// target的變化
displayLink = CADisplayLink(target: Proxy.with(target: self), selector: #selector(test))
// timer = Timer.scheduledTimer(timeInterval: 1, target: Proxy.with(target: self), selector: #selector(test), userInfo: nil, repeats: true)

總結(jié)

一、為什么會(huì)產(chǎn)生循環(huán)引用?

a. 當(dāng)使用block時(shí),沒(méi)有使用weak self,block會(huì)對(duì)self強(qiáng)引用。
b. 當(dāng)使用target時(shí),NSTimer內(nèi)部會(huì)對(duì)self產(chǎn)生強(qiáng)引用(repeats: YES)
注意:當(dāng)repeats為NO時(shí),不會(huì)產(chǎn)生循環(huán)引用

二、如果避免強(qiáng)引用問(wèn)題?

a. 使用block + weak self的方式,可以避免循環(huán)引用
b. 使用target的時(shí)候,使用代理對(duì)象,代理對(duì)象里面的target屬性對(duì)self弱引用,再利用消息轉(zhuǎn)發(fā)機(jī)制,將消息轉(zhuǎn)發(fā)給self去執(zhí)行selector方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容