MLeaksFinder原理:MLeaksFinder 一開始從 UIViewController 入手萌丈。我們知道昵时,當(dāng)一個 UIViewController 被 pop
或 dismiss 后顺献,該 UIViewController 包括它的 view,view 的 subviews
等等將很快被釋放(除非你把它設(shè)計成單例厌衙,或者持有它的強引用畦木,但一般很少這樣做)。于是申窘,我們只需在一個 ViewController 被 pop 或
dismiss 一小段時間后弯蚜,看看該 UIViewController,它的 view剃法,view 的 subviews 等等是否還存在碎捺。
具體的方法是,為基類 NSObject 添加一個方法-willDealloc方法,該方法的作用是收厨,先用一個弱指針指向 self晋柱,并在一小段時間(3秒)后,通過這個弱指針調(diào)用-assertNotDealloc诵叁,而-assertNotDealloc主要作用是直接中斷言雁竞。
- (BOOL)willDealloc {
__weakidweakSelf =self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
? ? ? ? [weakSelf assertNotDealloc];
? ? });
returnYES;
}
- (void)assertNotDealloc {
NSAssert(NO, @“”);
}
這樣,當(dāng)我們認(rèn)為某個對象應(yīng)該要被釋放了拧额,在釋放前調(diào)用這個方法碑诉,如果3秒后它被釋放成功,weakSelf 就指向 nil侥锦,不會調(diào)用到-assertNotDealloc方法联贩,也就不會中斷言,如果它沒被釋放(泄露了)捎拯,-assertNotDealloc就會被調(diào)用中斷言泪幌。這樣,當(dāng)一個 UIViewController 被 pop 或 dismiss 時(我們認(rèn)為它應(yīng)該要被釋放了)署照,我們遍歷該 UIViewController 上的所有 view祸泪,依次調(diào)-willDealloc,若3秒后沒被釋放建芙,就會中斷言没隘。
在這里,有幾個問題需要解決:
1.不入侵開發(fā)代碼
這里使用了 AOP 技術(shù)禁荸,hook 掉 UIViewController 和 UINavigationController 的 pop 跟 dismiss 方法右蒲,關(guān)于如何 hook,請參考Method Swizzling赶熟。
2.遍歷相關(guān)對象
在實際項目中瑰妄,我們發(fā)現(xiàn)有時候一個 UIViewController 被釋放了,但它的 view 沒被釋放映砖,或者一個 UIView
被釋放了间坐,但它的某個 subview 沒被釋放。這種內(nèi)存泄露的情況很常見邑退,因此竹宋,我們有必要遍歷基于 UIViewController 的整棵
View-ViewController 樹。我們通過 UIViewController 的 presentedViewController 和
view 屬性地技,UIView 的 subviews 屬性等遞歸遍歷蜈七。對于某些 ViewController,如
UINavigationController莫矗,UISplitViewController 等飒硅,我們還需要遍歷 viewControllers
屬性砂缩。
3.構(gòu)建堆棧信息
需要構(gòu)建 View-ViewController stack 信息以告訴開發(fā)者是哪個對象沒被釋放。在遞歸遍歷 View-ViewController 樹時狡相,子節(jié)點的 stack 信息由父節(jié)點的 stack 信息加上子結(jié)點信息即可梯轻。
4.例外機(jī)制
對于有些 ViewController食磕,在被 pop 或 dismiss 后尽棕,不會被釋放(比如單例),因此需要提供機(jī)制讓開發(fā)者指定哪個對象不會被釋放彬伦,這里可以通過重載上面的-willDealloc方法滔悉,直接 return NO 即可。
5.特殊情況
對于某些特殊情況单绑,釋放的時機(jī)不大一樣(比如系統(tǒng)手勢返回時回官,在劃到一半時 hold 住,雖然已被 pop搂橙,但這時還不會被釋放歉提,ViewController 要等到完全 disappear 后才釋放),需要做特殊處理区转,具體的特殊處理視具體情況而定苔巨。
6.系統(tǒng)View
某些系統(tǒng)的私有 View,不會被釋放(可能是系統(tǒng) bug 或者是系統(tǒng)出于某些原因故意這樣做的废离,這里就不去深究了)侄泽,因此需要建立白名單
7.手動擴(kuò)展
MLeaksFinder目前只檢測 ViewController 跟 View 對象。為此蜻韭,MLeaksFinder
提供了一個手動擴(kuò)展的機(jī)制悼尾,你可以從 UIViewController 跟 UIView
出發(fā),去檢測其它類型的對象的內(nèi)存泄露肖方。如下所示闺魏,我們可以檢測 UIViewController 底下的 View Model:
- (BOOL)willDealloc {
if(![superwillDealloc]) {
returnNO;
? ? }
MLCheck(self.viewModel);
returnYES;
}
這里的原理跟上面的是一樣的,宏 MLCheck() 做的事就是為傳進(jìn)來的對象建立 View-ViewController stack 信息俯画,并對傳進(jìn)來的對象調(diào)用-willDealloc方法舷胜。
、活翩、烹骨、、材泄、沮焕、、拉宗、峦树、辣辫、、魁巩、急灭、、谷遂、葬馋、、肾扰、畴嘶、、集晚、窗悯、、偷拔、蒋院、、莲绰、欺旧、、钉蒲、切端、、顷啼、踏枣、、钙蒙、茵瀑、、躬厌、马昨、、扛施、鸿捧、
AOP:在運行時,動態(tài)地將代碼切入到類的指定方法疙渣、指定位置上的編程思想就是面向切面的編程.向切面編程(AOP是Aspect Oriented Program的首字母縮寫) 匙奴,我們知道,面向?qū)ο蟮奶攸c是繼承妄荔、多態(tài)和封裝泼菌。而封裝就要求將功能分散到不同的對象中去谍肤,這在軟件設(shè)計中往往稱為職責(zé)分配。實際上也就是說哗伯,讓不同的類設(shè)計不同的方法荒揣。這樣代碼就分散到一個個的類中去了。這樣做的好處是降低了代碼的復(fù)雜程度焊刹,使類可重用系任。
但是人們也發(fā)現(xiàn),在分散代碼的同時伴澄,也增加了代碼的重復(fù)性赋除。什么意思呢阱缓?比如說非凌,我們在兩個類中,可能都需要在每個方法中做日志荆针。按面向?qū)ο蟮脑O(shè)計方法敞嗡,我們就必須在兩個類的方法中都加入日志的內(nèi)容。也許他們是完全相同的航背,但就是因為面向?qū)ο蟮脑O(shè)計讓類與類之間無法聯(lián)系喉悴,而不能將這些重復(fù)的代碼統(tǒng)一起來。
也許有人會說玖媚,那好辦啊箕肃,我們可以將這段代碼寫在一個獨立的類獨立的方法里,然后再在這兩個類中調(diào)用今魔。但是勺像,這樣一來,這兩個類跟我們上面提到的獨立的類就有耦合了错森,它的改變會影響這兩個類吟宦。那么,有沒有什么辦法涩维,能讓我們在需要的時候殃姓,隨意地加入代碼呢?這種在運行時瓦阐,動態(tài)地將代碼切入到類的指定方法蜗侈、指定位置上的編程思想就是面向切面的編程。
一般而言睡蟋,我們管切入到指定類指定方法的代碼片段稱為切面踏幻,而切入到哪些類、哪些方法則叫切入點薄湿。有了AOP叫倍,我們就可以把幾個類共有的代碼偷卧,抽取到一個切片中,等到需要時再切入對象中去吆倦,從而改變其原有的行為听诸。
這樣看來,AOP其實只是OOP的補充而已蚕泽。OOP從橫向上區(qū)分出一個個的類來晌梨,而AOP則從縱向上向?qū)ο笾屑尤胩囟ǖ拇a。有了AOP须妻,OOP變得立體了仔蝌。如果加上時間維度,AOP使OOP由原來的二維變?yōu)槿S了荒吏,由平面變成立體了敛惊。從技術(shù)上來說,AOP基本上是通過代理機(jī)制實現(xiàn)的绰更。
AOP在編程歷史上可以說是里程碑式的瞧挤,對OOP編程是一種十分有益的補充。
鏈接:https://www.zhihu.com/question/24863332/answer/48376158
MLeaksFinder:http://wereadteam.github.io/2016/02/22/MLeaksFinder/