iOS內(nèi)存泄漏檢測-MLeaksFinder學(xué)習(xí)及使用

小總結(jié)

MLeaksFinder 是 WeRead 團(tuán)隊(duì)開源的iOS內(nèi)存泄漏檢測工具固惯,wereadteam博客芯侥,GitHub躏将。

MLeaksFinder 個(gè)人認(rèn)為優(yōu)勢是在開發(fā)過程中自動(dòng)檢測 UIViewController 和 UIView 等UI對(duì)象內(nèi)存泄露陌知,及自定義是否檢測 UIViewController 中強(qiáng)引用的 NSObject 類對(duì)象內(nèi)存泄露京闰。這樣在開發(fā)調(diào)試過程中就能實(shí)際避免大部分內(nèi)存泄漏并及時(shí)修正編碼習(xí)慣颜及。

小學(xué)習(xí)

以下均轉(zhuǎn)自wereadteam博客

1. Leaks -> 從蘋果的開發(fā)者文檔里可以看到,一個(gè) app 的內(nèi)存分三類:
  • Leaked memory: Memory unreferenced by your application that cannot be used again or freed (also detectable by using the Leaks instrument). 泄漏內(nèi)存應(yīng)用程序沒有引用的內(nèi)存蹂楣,不能再次使用或釋放(也可以通過使用泄漏工具檢測到)俏站。

  • Abandoned memory: Memory still referenced by your application that has no useful purpose.廢棄內(nèi)存:內(nèi)存仍然被應(yīng)用程序引用,沒有任何有用的用途痊土。

  • Cached memory: Memory still referenced by your application that might be used again for better performance.緩存內(nèi)存:您的應(yīng)用程序仍然引用的內(nèi)存乾翔,這些內(nèi)存可能會(huì)再次用于更好的性能。

以上 Leaked memoryAbandoned memory 都屬于應(yīng)該釋放而沒釋放的內(nèi)存施戴,都是內(nèi)存泄露

2.說說 Instrument 的 Leaks / Allocations
  • Leaks 工具只負(fù)責(zé)檢測 Leaked memory反浓,而不管 Abandoned memory。在 MRC 時(shí)代Leaked memory 很常見赞哗,因?yàn)楹苋菀淄苏{(diào)用 release雷则,但在 ARC 時(shí)代更常見的內(nèi)存泄露是循環(huán)引用導(dǎo)致的 Abandoned memory,Leaks 工具查不出這類內(nèi)存泄露肪笋,應(yīng)用有限月劈。

  • 用 Instrument 的 Allocations 檢測Abandoned memory 。
    檢測方法是用 Mark Generation 的方式藤乙,當(dāng)你每次點(diǎn)擊 Mark Generation 時(shí)猜揪,Allocations 會(huì)生成當(dāng)前 App 的內(nèi)存快照,而且 Allocations 會(huì)記錄從上回內(nèi)存快照到這次內(nèi)存快照這個(gè)時(shí)間段內(nèi)坛梁,新分配的內(nèi)存信息而姐。舉一個(gè)最簡單的例子:

    我們可以不斷重復(fù) push 和 pop 同一個(gè) UIViewController,理論上來說划咐,push 之前跟 pop 之后拴念,app 會(huì)回到相同的狀態(tài)。因此褐缠,在 push 過程中新分配的內(nèi)存政鼠,在 pop 之后應(yīng)該被 dealloc 掉,除了前幾次 push 可能有預(yù)熱數(shù)據(jù)和 cache 數(shù)據(jù)的情況队魏。如果在數(shù)次 push 跟 pop 之后公般,內(nèi)存還不斷增長,則有內(nèi)存泄露。因此官帘,我們在每回 push 之前跟 pop 之后瞬雹,都 Mark Generation 一下,以此觀察內(nèi)存是不是無限制增長遏佣。

缺點(diǎn) 用這種方法來發(fā)現(xiàn)內(nèi)存泄露還是很不方便的:
首先挖炬,你得打開 Allocations
其次,你得一個(gè)個(gè)場景去重復(fù)的操作
無法及時(shí)得知泄露状婶,得專門做一遍上述操作意敛,十分繁瑣

3. 原理學(xué)習(xí)
  • MLeaksFinder 一開始從 UIViewController 入手。我們知道膛虫,當(dāng)一個(gè) UIViewController 被 pop 或 dismiss 后草姻,該 UIViewController 包括它的 view,view 的 subviews 等等將很快被釋放(除非你把它設(shè)計(jì)成單例稍刀,或者持有它的強(qiáng)引用撩独,但一般很少這樣做)。于是账月,我們只需在一個(gè) ViewController 被 pop 或 dismiss 一小段時(shí)間后综膀,看看該 UIViewController,它的 view局齿,view 的 subviews 等等是否還存在剧劝。

  • 具體的方法是,為基類 NSObject 添加一個(gè)方法 -willDealloc 方法抓歼,該方法的作用是讥此,先用一個(gè)弱指針指向 self,并在一小段時(shí)間(3秒)后谣妻,通過這個(gè)弱指針調(diào)用 -assertNotDealloc萄喳,而 -assertNotDealloc 主要作用是直接中斷言。

- (BOOL)willDealloc {
    __weak id weakSelf = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        __strong id strongSelf = weakSelf;
        [strongSelf assertNotDealloc];
    });
    
    return YES;
}
- (void)assertNotDealloc {
     NSAssert(NO, @“”);
}

這樣蹋半,當(dāng)我們認(rèn)為某個(gè)對(duì)象應(yīng)該要被釋放了他巨,在釋放前調(diào)用這個(gè)方法,如果3秒后它被釋放成功湃窍,weakSelf 就指向 nil闻蛀,不會(huì)調(diào)用到 -assertNotDealloc 方法,也就不會(huì)中斷言您市,如果它沒被釋放(泄露了),-assertNotDealloc 就會(huì)被調(diào)用中斷言役衡。這樣茵休,當(dāng)一個(gè) UIViewController 被 pop 或 dismiss 時(shí)(我們認(rèn)為它應(yīng)該要被釋放了),我們遍歷該 UIViewController 上的所有 view,依次調(diào) -willDealloc榕莺,若2秒后沒被釋放俐芯,就會(huì)斷言。

4. 代碼中解決的問題
  • 不入侵開發(fā)代碼
    這里使用了 AOP 技術(shù)钉鸯,hook 掉 UIViewController 和 UINavigationController 的 pop 跟 dismiss 方法吧史,關(guān)于如何 hook,請(qǐng)參考 Method Swizzling唠雕。

  • 遍歷相關(guān)對(duì)象
    在實(shí)際項(xiàng)目中贸营,我們發(fā)現(xiàn)有時(shí)候一個(gè) UIViewController 被釋放了,但它的 view 沒被釋放岩睁,或者一個(gè) UIView 被釋放了钞脂,但它的某個(gè) subview 沒被釋放。這種內(nèi)存泄露的情況很常見捕儒,因此冰啃,我們有必要遍歷基于 UIViewController 的整棵 View-ViewController 樹。我們通過 UIViewController 的 presentedViewController 和 view 屬性刘莹,UIView 的 subviews 屬性等遞歸遍歷阎毅。對(duì)于某些 ViewController,如 UINavigationController点弯,UISplitViewController 等扇调,我們還需要遍歷 viewControllers 屬性。

  • 構(gòu)建堆棧信息
    需要構(gòu)建 View-ViewController stack 信息以告訴開發(fā)者是哪個(gè)對(duì)象沒被釋放蒲拉。在遞歸遍歷 View-ViewController 樹時(shí)肃拜,子節(jié)點(diǎn)的 stack 信息由父節(jié)點(diǎn)的 stack 信息加上子結(jié)點(diǎn)信息即可。

  • 例外機(jī)制
    對(duì)于有些 ViewController雌团,在被 pop 或 dismiss 后燃领,不會(huì)被釋放(比如單例),因此需要提供機(jī)制讓開發(fā)者指定哪個(gè)對(duì)象不會(huì)被釋放锦援,這里可以通過重載上面的 -willDealloc 方法猛蔽,直接 return NO 即可。

  • 特殊情況
    對(duì)于某些特殊情況灵寺,釋放的時(shí)機(jī)不大一樣(比如系統(tǒng)手勢返回時(shí)曼库,在劃到一半時(shí) hold 住,雖然已被 pop略板,但這時(shí)還不會(huì)被釋放毁枯,ViewController 要等到完全 disappear 后才釋放),需要做特殊處理叮称,具體的特殊處理視具體情況而定种玛。

  • 系統(tǒng)View
    某些系統(tǒng)的私有 View藐鹤,不會(huì)被釋放(可能是系統(tǒng) bug 或者是系統(tǒng)出于某些原因故意這樣做的,這里就不去深究了)赂韵,因此需要建立白名單

  • 手動(dòng)擴(kuò)展
    MLeaksFinder目前只檢測 ViewController 跟 View 對(duì)象娱节。為此,MLeaksFinder 提供了一個(gè)手動(dòng)擴(kuò)展的機(jī)制祭示,你可以從 UIViewController 跟 UIView 出發(fā)肄满,去檢測其它類型的對(duì)象的內(nèi)存泄露。如下所示质涛,我們可以檢測 UIViewController 底下的 View Model:

- (BOOL)willDealloc {
    if (![super willDealloc]) {
        return NO;
    }
    MLCheck(self.viewModel);
    return YES;
}

這里的原理跟上面的是一樣的稠歉,宏 MLCheck() 做的事就是為傳進(jìn)來的對(duì)象建立 View-ViewController stack 信息,并對(duì)傳進(jìn)來的對(duì)象調(diào)用 -willDealloc 方法蹂窖。

  • 查找循環(huán)引用鏈

Facebook 開源了的循環(huán)引用檢測工具 FBRetainCycleDetector轧抗。當(dāng)傳入內(nèi)存中的任意一個(gè) OC 對(duì)象,F(xiàn)BRetainCycleDetector 會(huì)遞歸遍歷該對(duì)象的所有強(qiáng)引用的對(duì)象瞬测,以檢測以該對(duì)象為根結(jié)點(diǎn)的強(qiáng)引用樹有沒有循環(huán)引用横媚。

我們知道,很多循環(huán)引用是 block 的使用不當(dāng)造成的月趟。而 FBRetainCycleDetector 最大的技術(shù)亮點(diǎn)灯蝴,正在于如何找出一個(gè) block 的所有強(qiáng)引用對(duì)象。

然而孝宗,F(xiàn)BRetainCycleDetector 的使用存在兩個(gè)問題:
需要找到候選的檢測對(duì)象
檢測循環(huán)引用比較耗時(shí)

正是由于這兩個(gè)問題穷躁,F(xiàn)BRetainCycleDetector 通常是結(jié)合其它工具一起使用,通過其它工具先找出候選的檢測對(duì)象因妇,然后進(jìn)行有選擇的檢測问潭。當(dāng) MLeaksFinder 與 FBRetainCycleDetector 結(jié)合使用時(shí),正好能達(dá)到很好的效果婚被。我們先通過 MLeaksFinder 找到內(nèi)存泄漏的對(duì)象狡忙,然后再過 FBRetainCycleDetector 檢測該對(duì)象有沒有循環(huán)引用即可。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末址芯,一起剝皮案震驚了整個(gè)濱河市灾茁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谷炸,老刑警劉巖北专,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異旬陡,居然都是意外死亡拓颓,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門描孟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來录粱,“玉大人腻格,你說我怎么就攤上這事画拾∩斗保” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵青抛,是天一觀的道長旗闽。 經(jīng)常有香客問我,道長蜜另,這世上最難降的妖魔是什么适室? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮举瑰,結(jié)果婚禮上捣辆,老公的妹妹穿的比我還像新娘。我一直安慰自己此迅,他們只是感情好汽畴,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著耸序,像睡著了一般忍些。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上坎怪,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天罢坝,我揣著相機(jī)與錄音,去河邊找鬼搅窿。 笑死嘁酿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的男应。 我是一名探鬼主播闹司,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼殉了!你這毒婦竟也來了开仰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤薪铜,失蹤者是張志新(化名)和其女友劉穎众弓,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體隔箍,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谓娃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蜒滩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滨达。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奶稠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出捡遍,到底是詐尸還是另有隱情锌订,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布画株,位于F島的核電站辆飘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏谓传。R本人自食惡果不足惜蜈项,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望续挟。 院中可真熱鬧紧卒,春花似錦、人聲如沸诗祸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贬媒。三九已至聋亡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間际乘,已是汗流浹背坡倔。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脖含,地道東北人罪塔。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像养葵,于是被迫代替她去往敵國和親征堪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容