MLeaksFinder

MLeaksFinder

是 iOS 平臺(tái)的自動(dòng)內(nèi)存泄漏檢測(cè)工具,引進(jìn) MLeaksFinder 后,就可以在日常的開(kāi)發(fā),調(diào)試業(yè)務(wù)邏輯的過(guò)程中自動(dòng)地發(fā)現(xiàn)并警告內(nèi)存泄漏柴梆。開(kāi)發(fā)者無(wú)需打開(kāi) instrument 等工具,也無(wú)需為了找內(nèi)存泄漏而去跑額外的流程终惑。并且绍在,由于開(kāi)發(fā)者是在修改代碼之后一跑業(yè)務(wù)邏輯就能發(fā)現(xiàn)內(nèi)存泄漏的,這使得開(kāi)發(fā)者能很快地意識(shí)到是哪里的代碼寫(xiě)得問(wèn)題雹有。這種及時(shí)的內(nèi)存泄漏的發(fā)現(xiàn)在很大的程度上降低了修復(fù)內(nèi)存泄漏的成本偿渡。

MLeaksFinder 0.1 開(kāi)源已經(jīng)有一段時(shí)間,關(guān)于 MLeaksFinder 的基本原理霸奕,可以參考這篇文章溜宽。在 MLeaksFinder 開(kāi)源之后,收到的最多的反饋是:MLeaksFinder 幫忙發(fā)現(xiàn)了內(nèi)存泄漏质帅,但是要去修復(fù)這些內(nèi)存泄漏适揉,找到造成問(wèn)題的代碼很難,特別是對(duì)于歷史遺留的內(nèi)存泄漏煤惩。

現(xiàn)在嫉嘀,MLeaksFinder 0.2 來(lái)了。如果說(shuō) 0.1 版本旨在幫助開(kāi)發(fā)者發(fā)現(xiàn)內(nèi)存泄漏魄揉,那么 0.2 版本的新特性剪侮,正是旨在幫助開(kāi)發(fā)者更好地解決內(nèi)存泄漏。MLeaksFinder 0.2 包括以下幾個(gè)新特性:

  • assert 改為 alert
  • 追蹤對(duì)象的生命周期
  • 查找循環(huán)引用鏈

下面洛退,我們來(lái)逐一看一下這幾個(gè)特性票彪。

<a id="more" style="background-color: rgb(255, 255, 255); color: rgb(85, 85, 85); text-decoration: none; border-bottom: 1px solid rgb(204, 204, 204); word-wrap: break-word; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;"></a>

assert 改為 alert

在 MLeaksFiner 0.1 版本,當(dāng) MLeaksFinder 發(fā)現(xiàn)內(nèi)存泄漏時(shí)不狮,會(huì)直接中 assert 并打出內(nèi)存泄漏的信息。Assert 能迫使開(kāi)發(fā)者及時(shí)地去修復(fù)內(nèi)存泄漏在旱,并且摇零,如果只是打日志,內(nèi)存泄漏的日志很可能會(huì)被淹沒(méi)在眾多的日志中桶蝎。這種 assert 的方法在我們實(shí)際的項(xiàng)目取得了不錯(cuò)的效果驻仅。

然而谅畅,assert 確實(shí)也有不好的一面。當(dāng)開(kāi)發(fā)者在調(diào)試業(yè)務(wù)邏輯的過(guò)程中噪服,如果由于內(nèi)存泄漏中 assert 而使得整個(gè)程序掛掉了毡泻,那么開(kāi)發(fā)者的思維會(huì)因此被打斷,并不得不在修復(fù)完內(nèi)存泄漏之后粘优,從頭開(kāi)始調(diào)試業(yè)務(wù)邏輯仇味。有時(shí)候開(kāi)發(fā)者更希望的是連貫地調(diào)完整個(gè)業(yè)務(wù)邏輯之后,再回過(guò)頭來(lái)修復(fù)內(nèi)存泄漏雹顺。

因此丹墨,MLeaksFinder 0.2 把 assert 改成了 alert。當(dāng)發(fā)現(xiàn)內(nèi)存泄漏之后嬉愧,開(kāi)發(fā)者可以把 alert 框關(guān)掉菩暗,并繼續(xù)調(diào)試業(yè)務(wù)邏輯牍陌。而且,把 assert 改成 alert 之后,也使得進(jìn)一步分析內(nèi)存成為可能捆蜀,為下面兩個(gè)新特性墊定基礎(chǔ)。

追蹤對(duì)象的生命周期

當(dāng)發(fā)現(xiàn)可能的內(nèi)存泄漏對(duì)象并給出 alert 之后聊浅,MLeaksFinder 會(huì)進(jìn)一步地追蹤該對(duì)象的生命周期洪碳,并在該對(duì)象釋放時(shí)給出

Object Deallocated

的 alert。

為什么認(rèn)為一個(gè)對(duì)象內(nèi)存泄漏之后闪金,還要進(jìn)一步去追蹤該對(duì)象后續(xù)會(huì)不會(huì)釋放呢疯溺?MLeaksFinder 的基本原理是這樣的,當(dāng)一個(gè) ViewController 被 pop 或 dismiss 之后哎垦,我們認(rèn)為該 ViewController囱嫩,包括它上面的子 ViewController,以及它的 View漏设,View 的 subView 等等墨闲,都很快會(huì)被釋放,如果某個(gè) View 或者 ViewController 沒(méi)釋放郑口,我們就認(rèn)為該對(duì)象泄漏了鸳碧。然而,這樣的判斷內(nèi)存泄漏的方法存在兩個(gè)可能的“誤判”:

1) 單例或者被 cache 起來(lái)復(fù)用的 View 或 ViewController

對(duì)于這樣的 View 或 ViewController犬性,在被 pop 或 dismiss 之后是不會(huì)被釋放的瞻离。然而,由于 View 相關(guān)的對(duì)象一般都占用了較多了內(nèi)存乒裆,這樣的設(shè)計(jì)通常來(lái)說(shuō)不是好的設(shè)計(jì)套利。如果開(kāi)發(fā)者由于性能問(wèn)題等原因而不得不這樣設(shè)計(jì)的時(shí)候,開(kāi)發(fā)者可以在報(bào)泄漏的類(lèi)里重載

- (BOOL)willDealloc

方法,直接

return NO;

以消除內(nèi)存泄漏的警告肉迫,這個(gè)消除內(nèi)存泄漏警告的方法與 MLeaksFinder 0.1 版本一致验辞。

2) 釋放不及時(shí)的 View 或 ViewController

例如,發(fā)起網(wǎng)絡(luò)請(qǐng)求的時(shí)候喊衫,在網(wǎng)絡(luò)請(qǐng)求回調(diào)的 block 里強(qiáng)引用 ViewController跌造,以便在網(wǎng)絡(luò)請(qǐng)求回來(lái)的時(shí)候刷新界面。在網(wǎng)絡(luò)請(qǐng)求比較慢的情況下族购,這種做法存在兩個(gè)問(wèn)題:

  • ViewController 被 pop 之后壳贪,由于被 block 強(qiáng)引用導(dǎo)致釋放不及時(shí)
  • ViewController 被 pop 之后,如果網(wǎng)絡(luò)請(qǐng)求回來(lái)了联四,不應(yīng)該繼續(xù)做刷新界面的事撑碴,浪費(fèi) CPU

所以,對(duì)于這種情況朝墩,我們應(yīng)該在 block 里弱引用 ViewController醉拓,而不是強(qiáng)引用。

下面我們來(lái)看如何利用對(duì)象的生命周期來(lái)分析內(nèi)存的真正使用情況收苏,分三種情況:

1) 單例或者被 cache 起來(lái)復(fù)用

如下面所示亿卤,在第一次 pop 的時(shí)候報(bào) Memory Leak,在之后的重復(fù) push 并 pop 同一個(gè) ViewController 過(guò)程中鹿霸,即不報(bào) Object Deallocated排吴,也不報(bào) Memory Leak。這種情況下我們可以確定該對(duì)象被設(shè)計(jì)成單例或者 cache 起來(lái)了懦鼠。

<figure class="highlight objc" style="display: block; margin: 20px 0px; background: rgb(29, 31, 33); padding: 15px; overflow: auto; font-size: 13px; color: rgb(197, 200, 198); line-height: 1.6; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: "Input Mono", "PT Mono", Consolas, Monaco, Menlo, monospace; font-size: 13px; background: rgb(29, 31, 33); margin: 0px; padding: 1px 20px 1px 1px; color: rgb(102, 102, 102); line-height: 1.6; border: none; text-align: right;">

1

2

</pre>

|

<pre style="overflow: auto; font-family: "Input Mono", "PT Mono", Consolas, Monaco, Menlo, monospace; font-size: 13px; background: rgb(29, 31, 33); margin: 0px; padding: 1px; color: rgb(197, 200, 198); line-height: 1.6; border: none;">

pop             push           pop           push          pop

----------> Leak ----------> | ----------> | ----------> | ---------->

</pre>

|

</figure>

2) 釋放不及時(shí)

如下面所示钻哩,在第一次 pop 的時(shí)候報(bào) Memory Leak,在之后的重復(fù) push 并 pop 同一個(gè) ViewController 過(guò)程中肛冶,對(duì)于同一個(gè)類(lèi)不斷地報(bào) Object Deallocated 和 Memory Leak街氢。這種情況屬于釋放不及時(shí)的情況。

<figure class="highlight objc" style="display: block; margin: 20px 0px; background: rgb(29, 31, 33); padding: 15px; overflow: auto; font-size: 13px; color: rgb(197, 200, 198); line-height: 1.6; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: "Input Mono", "PT Mono", Consolas, Monaco, Menlo, monospace; font-size: 13px; background: rgb(29, 31, 33); margin: 0px; padding: 1px 20px 1px 1px; color: rgb(102, 102, 102); line-height: 1.6; border: none; text-align: right;">

1

2

</pre>

|

<pre style="overflow: auto; font-family: "Input Mono", "PT Mono", Consolas, Monaco, Menlo, monospace; font-size: 13px; background: rgb(29, 31, 33); margin: 0px; padding: 1px; color: rgb(197, 200, 198); line-height: 1.6; border: none;">

pop             push                 pop             push                 pop

----------> Leak ----------> Dealloc ----------> Leak ----------> Dealloc ----------> Leak

</pre>

|

</figure>

3) 真正的內(nèi)存泄漏

如下面所示睦袖,在第一次 pop 的時(shí)候報(bào) Memory Leak珊肃,在之后的重復(fù) push 并 pop 同一個(gè) ViewController 過(guò)程中,不報(bào) Object Deallocated馅笙,但每次 pop 之后又報(bào) Memory Leak伦乔。這種情況下每回進(jìn)入并退出一個(gè)頁(yè)面后,就報(bào)有新的內(nèi)存泄漏董习,同時(shí)被報(bào)泄漏的對(duì)象又從來(lái)沒(méi)有釋放過(guò)烈和,可以確定是真正的內(nèi)存泄漏。

<figure class="highlight objc" style="display: block; margin: 20px 0px; background: rgb(29, 31, 33); padding: 15px; overflow: auto; font-size: 13px; color: rgb(197, 200, 198); line-height: 1.6; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: "Input Mono", "PT Mono", Consolas, Monaco, Menlo, monospace; font-size: 13px; background: rgb(29, 31, 33); margin: 0px; padding: 1px 20px 1px 1px; color: rgb(102, 102, 102); line-height: 1.6; border: none; text-align: right;">

1

2

</pre>

|

<pre style="overflow: auto; font-family: "Input Mono", "PT Mono", Consolas, Monaco, Menlo, monospace; font-size: 13px; background: rgb(29, 31, 33); margin: 0px; padding: 1px; color: rgb(197, 200, 198); line-height: 1.6; border: none;">

pop             push           pop             push           pop

----------> Leak ----------> | ----------> Leak ----------> | ----------> Leak

</pre>

|

</figure>

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

Facebook 在前陣子開(kāi)源了一個(gè)循環(huán)引用檢測(cè)工具

FBRetainCycleDetector皿淋。當(dāng)傳入內(nèi)存中的任意一個(gè) OC 對(duì)象斥杜,F(xiàn)BRetainCycleDetector 會(huì)遞歸遍歷該對(duì)象的所有強(qiáng)引用的對(duì)象虱颗,以檢測(cè)以該對(duì)象為根結(jié)點(diǎn)的強(qiáng)引用樹(shù)有沒(méi)有循環(huán)引用。

我們知道蔗喂,很多循環(huán)引用是 block 的使用不當(dāng)造成的。而 FBRetainCycleDetector 最大的技術(shù)亮點(diǎn)高帖,正在于如何找出一個(gè) block 的所有強(qiáng)引用對(duì)象缰儿。對(duì)于這個(gè)感興趣的,可以看 facebook 的這篇文章散址。

然而乖阵,F(xiàn)BRetainCycleDetector 的使用存在兩個(gè)問(wèn)題:

  • 需要找到候選的檢測(cè)對(duì)象
  • 檢測(cè)循環(huán)引用比較耗時(shí)

正是由于這兩個(gè)問(wèn)題,F(xiàn)BRetainCycleDetector 通常是結(jié)合其它工具一起使用预麸,通過(guò)其它工具先找出候選的檢測(cè)對(duì)象瞪浸,然后進(jìn)行有選擇的檢測(cè)。當(dāng) MLeaksFinder 與 FBRetainCycleDetector 結(jié)合使用時(shí)吏祸,正好能達(dá)到很好的效果对蒲。我們先通過(guò) MLeaksFinder 找到內(nèi)存泄漏的對(duì)象,然后再過(guò) FBRetainCycleDetector 檢測(cè)該對(duì)象有沒(méi)有循環(huán)引用即可贡翘。

循環(huán)引用的輸出信息如下:

<figure class="highlight objc" style="display: block; margin: 20px 0px; background: rgb(29, 31, 33); padding: 15px; overflow: auto; font-size: 13px; color: rgb(197, 200, 198); line-height: 1.6; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: "Input Mono", "PT Mono", Consolas, Monaco, Menlo, monospace; font-size: 13px; background: rgb(29, 31, 33); margin: 0px; padding: 1px 20px 1px 1px; color: rgb(102, 102, 102); line-height: 1.6; border: none; text-align: right;">

1

2

3

4

</pre>

|

<pre style="overflow: auto; font-family: "Input Mono", "PT Mono", Consolas, Monaco, Menlo, monospace; font-size: 13px; background: rgb(29, 31, 33); margin: 0px; padding: 1px; color: rgb(197, 200, 198); line-height: 1.6; border: none;">

(

"-> MyTableViewCell "

,

"-> _callback -> NSMallocBlock "

)

</pre>

|

</figure>

上面的信息表示蹈矮,MyTableViewCell

有一個(gè)強(qiáng)引用的成員變量

_callback,該變量的類(lèi)型是

__NSMallocBlock__鸣驱,在

_callback

里泛鸟,又強(qiáng)引用了

MyTableViewCell

造成循環(huán)引用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末踊东,一起剝皮案震驚了整個(gè)濱河市北滥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌闸翅,老刑警劉巖再芋,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異缎脾,居然都是意外死亡祝闻,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)遗菠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)联喘,“玉大人,你說(shuō)我怎么就攤上這事辙纬』碓猓” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵贺拣,是天一觀的道長(zhǎng)蓖谢。 經(jīng)常有香客問(wèn)我捂蕴,道長(zhǎng),這世上最難降的妖魔是什么闪幽? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任啥辨,我火速辦了婚禮,結(jié)果婚禮上盯腌,老公的妹妹穿的比我還像新娘溉知。我一直安慰自己,他們只是感情好腕够,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布级乍。 她就那樣靜靜地躺著,像睡著了一般帚湘。 火紅的嫁衣襯著肌膚如雪玫荣。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,821評(píng)論 1 290
  • 那天大诸,我揣著相機(jī)與錄音捅厂,去河邊找鬼。 笑死底挫,一個(gè)胖子當(dāng)著我的面吹牛恒傻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播建邓,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼盈厘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了官边?” 一聲冷哼從身側(cè)響起沸手,我...
    開(kāi)封第一講書(shū)人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎注簿,沒(méi)想到半個(gè)月后契吉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡诡渴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年捐晶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妄辩。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惑灵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出眼耀,到底是詐尸還是另有隱情英支,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布哮伟,位于F島的核電站干花,受9級(jí)特大地震影響妄帘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜池凄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一抡驼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧修赞,春花似錦婶恼、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蚣录。三九已至割择,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間萎河,已是汗流浹背荔泳。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虐杯,地道東北人玛歌。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像擎椰,于是被迫代替她去往敵國(guó)和親支子。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

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