發現循環引用的方法

做者:Thomas Hanning,原文連接,原文日期:2015-11-09
譯者:靛青K;校對:littledogboy;定稿:shanksswift

儘管 ARC 已經爲咱們作了大部份內存管理的事情,但你的 App 仍然可能遇到循環引用的問題。所以發現這些潛在的循環引用是很是重要的。ide

ARC 和 內存管理

隨着在 iOS 5 中 介紹的自動引用計數(ARC)的使用,內存管理變得很是簡單。但 ARC 不能處理全部狀況,因此處理好 App(應用程序) 的內存管理仍是很是重要的。例如,可能存在所謂的循環引用。就好比在應用程序中儘管沒有任何可訪問的引用指向視圖控制器,但視圖控制器也沒有被銷燬。若是存在這種循環引用,那麼每次出現這個視圖控制器,應用程序的內存都會增長。若是內存不停地增長,App 會被操做系統終止 —— App 崩潰。工具

循環引用

咱們來建立一個循環引用的例子:首先,咱們建立了一個 RootViewController 和一個 SecondViewController。當點擊 RootViewController 的一個按鈕後,就出現 (present)一個 SecondViewController。你能夠在 storyboard 中經過 segue 輕鬆建立。 另外,再建立一個 ModelObject 類,該類中有一個類型爲 ModelObjectDelegate 的 delegate 實例變量 。當 SecondViewController 加載完視圖後,把 ModelObject 的代理設置爲selfui

import Foundation

protocol ModelObjectDelegate: class {
    
}

class ModelObject {
    
    var delegate: ModelObjectDelegate?
       
}


import UIKit

class SecondViewController: UIViewController, ModelObjectDelegate {
    
    var modelObject: ModelObject?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        modelObject = ModelObject()
        modelObject!.delegate = self
    }
        
    @IBAction func closeButtonPressed(sender: UIButton) {
        dismissViewControllerAnimated(true, completion: nil)
    }
    
}

好的,如今咱們來檢查一下內存管理:當咱們移除SecondViewController時,內存並不會減小。但這是爲何呢?
咱們預期的結果是移除時,SecondViewController內存就會銷燬。咱們來看一下這些對象。當SecondViewController加載後,引用狀況是下圖這個樣子:spa

如今,當移除SecondViewController時,引用狀況是這個樣子:操作系統

RootViewController 取消了強引用 SecondViewController。然而 SecondViewControllerModelObject 互相強引用。所以它們都沒有被銷燬。翻譯

技巧

發現循環引用的技巧以下所示: 代理

若是一個對象被銷燬,會執行對應的 deinit 方法。因此只須要在類中的 deinit 方法中添加一個打印信息:調試

import UIKit

class SecondViewController: UIViewController, ModelObjectDelegate {
    
    var modelObject: ModelObject?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        modelObject = ModelObject()
        modelObject!.delegate = self
    }
    
    @IBAction func closeButtonPressed(sender: UIButton) {
        dismissViewControllerAnimated(true, completion: nil)
    }
    
    deinit {
        print("SecondViewController deinit")
    }
}


import Foundation

protocol ModelObjectDelegate: class {
    
}

class ModelObject {
    
    var delegate: ModelObjectDelegate?
    
    deinit {
        print("ModelObject deinit")
    }
    
}

當咱們移除 SecondViewController,調試窗口並無日誌信息。也就是說他們並無被銷燬,說明出了些問題。日誌

解決辦法

咱們已經知道這裏多了一個強引用。因此咱們能夠聲明delegateweak屬性。

import Foundation

protocol ModelObjectDelegate: class {
    
}

class ModelObject {
    
    weak var delegate: ModelObjectDelegate?
    
    deinit {
        print("ModelObject deinit")
    }
    
}

如今的對象關係是這個樣子:

由於 SecondViewControllerModelObject 之間僅有一個強引用,這裏應該不會再有什麼問題了。

沒錯,如今當咱們移除SecondViewController調試窗口會有日誌信息了:

SecondViewController deinit
ModelObject deinit

如今和咱們的預期狀況同樣了。

結論

儘管這是一點點的工做,爲了發現循環引用應該在視圖控制器的deinit方法中添加一條日誌信息。你也可使用 Instruments(Xcode自帶的內存檢測工具) 發現循環引用,但若是老是把日誌信息放在deinit方法中,能夠持續監測銷燬行爲了。

本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg

相關文章
相關標籤/搜索