在 iOS 開發(fā)中,內(nèi)存泄漏的檢測基本是 APP 功能開發(fā)完成之后的必做項目。內(nèi)存泄漏的檢測手段很多,這里就講講如何通過 Instruments 的內(nèi)存工具來定位內(nèi)存問題。
案例
內(nèi)存檢測以 raywenderlich 的 Catstagram APP 作為分析案例。該 APP包含 2 個頁面,第一個頁面是一個按鈕,第二個頁面是一個包含圖片的列表,APP 截圖如下。


啟動 APP,然后點擊紅色按鈕進入列表頁面,然后退出列表頁面。這么做的原因的是什么呢?因為要檢測的是列表頁面的內(nèi)存泄漏問題。所以進入列表頁面之后再退出列表頁面回到第一個頁面,若是列表頁面存在內(nèi)存問題,那么必然是有些對象無法被釋放或者該列表頁面無法被銷毀,這個時候用工具就可以查出對應(yīng)的內(nèi)存問題。
Debug Menory Graph
先使用 Xcode(我的 Xcode 版本是 8.3.2) 的 Debug Menory Graph 功能對 APP 的內(nèi)存泄漏情況做個整體瀏覽。
點擊下圖箭頭所示的按鈕,顯示 Debug Menory Graph 界面。


從上圖可以看到,LocationModel,PhotoFeedModel,PhotoModel,UserModel 類的對象在圖中都有一個 ! 號,這意味著這些對象都是存在內(nèi)存問題的,點擊對應(yīng)的 PhotoFeedModel 類的對象,右邊顯示的是該類的對象的引用情況,可以看到 PhotoFeedModel 有循環(huán)引用問題。既然已經(jīng)知道了哪些對象有內(nèi)存問題,但是這些對象的使用代碼到處都是,要定位問題,光只有這些信息還遠遠不夠,最好是能夠定位到某個方法或者某行代碼,但這個不是 Debug Menory Graph 的功能。
使用 Leaks 和 Allocations
啟動 Instruments,選擇 Leaks 模板,Instruments 知道要分析內(nèi)存問題,所以同時啟動了 Allocations 模板。


啟動 Instruments 開始錄制 APP 運行情況,重復(fù)進入退出列表,出現(xiàn)了內(nèi)存泄漏了情況。打叉表示有內(nèi)存泄漏。

咋一看信息量有點大,那么我們先過濾一下信息,按箭頭所示,選擇 Call Tree 視圖,然后過濾掉系統(tǒng)庫,展開方法調(diào)用信息。通過內(nèi)存泄漏的時間段查看這段時間的方法調(diào)用,然后對這些方法的代碼進行排查,查找代碼問題。


經(jīng)過方法排查,定位到了 CatPhotoTableViewCell 的 reverseGeocode 方法,由于方法 reverseGeocode 沒有對 self 和 photoModel 變量做 weak 弱引用聲明。
func reverseGeocode(locationForPhoto photoModel: PhotoModel) {
photoModel.location?.reverseGeocodedLocation(completion: { (locationModel) in
self.photoLocationLabel.attributedText = photoModel.locationAttributedString(withFontSize: 14.0)
self.photoLocationLabel.attributedText = photoModel.locationAttributedString(withFontSize: 14.0)
self.photoLocationLabel.sizeToFit()
DispatchQueue.main.async {
self.updateConstraints()
self.setNeedsLayout()
}
})
}
修改后如下
func reverseGeocode(locationForPhoto photoModel: PhotoModel) {
photoModel.location?.reverseGeocodedLocation(completion: { [weak self, weak photoModel] (locationModel) in
self?.photoLocationLabel.attributedText = photoModel?.locationAttributedString(withFontSize: 14.0)
self?.photoLocationLabel.attributedText = photoModel?.locationAttributedString(withFontSize: 14.0)
self?.photoLocationLabel.sizeToFit()
DispatchQueue.main.async {
self?.updateConstraints()
self?.setNeedsLayout()
}
})
}
修改之后對修改進行驗證,結(jié)果說明我們的修改是正確的。

總結(jié)
內(nèi)存泄漏的解決方法一般步驟
1、使用 Debug Menory Graph 發(fā)現(xiàn)問題
2、使用 Instruments 的 Leaks 和 Allocations 定位問題
3、解決問題
4、使用 Debug Menory Graph 驗證修改效果
5、若是驗證不通過,重復(fù) 1 - 4 步驟。若是驗證通過則結(jié)束。
參考
本文是 raywenderlich 的課程筆記,內(nèi)容參考 Practical Instruments 課程
1、 demo https://files.betamax.raywenderlich.com/attachments/videos/787/a455ee9d-a5c2-4e1c-9a48-10bf86cbc2b9.zip
2、 https://videos.raywenderlich.com/courses/74-practical-instruments/lessons/5