一般引起內(nèi)存泄露的可能
1、delegate使用strong引用,如果父VC持有子VC,并設(shè)置VC的delegate為self(也就是父VC),這樣的結(jié)果就是子VC也間接持有了父VC,造成了循環(huán)引用,在Pop子VC的時(shí)候不會(huì)調(diào)用dealloc
2、timer是否沒(méi)有invalidate,scheduledTimerWithTimeInterval:target:selector:userInfo:
這里的target一般是self,這個(gè)時(shí)候就需要注意了,在退出時(shí)候,如果timer還在執(zhí)行的話,有timer會(huì)持有self,所以也不會(huì)調(diào)dealloc
確保timer有invalidate,或使用弱引用weakSelf來(lái)替代self
3、最常見(jiàn)的其實(shí)是block引起的循環(huán)引用問(wèn)題
block的引用分以下三種情況
1)不作為成員變量
[self oneBlockSucess:^{
????????[self doSomething];
?}];
2)成員變量,直接引用
self.secondBlock = ^{?
????????[self doSomething];?
};
3)成員變量,間接引用
typedef void(^BtnPressedBlock)(UIButton*btn);
@interface XXSubmitBottomView:UIView
@property(nonatomic,copy)BtnPressedBlock block;
-(void)submittBtnPressed:(BtnPressedBlock)block;
@end
@implementation XXConfirmOrderController
if(!_bottomView) {
?????????_bottomView = [[XXSubmitBottomView alloc] initWithFrame:CGRectMake(0,self.view.height -50, Width,50)];?
?????????_bottomView.currentVc =self;
#warning self.bottomView.block self間接持有了BtnPressedBlock 必須使用weak!
WEAKSELF//ps: weakSelf的宏定義#define WEAKSELF typeof(self) __weak weakSelf = self;
????????[_bottomView submittBtnPressed:^(UIButton*btn) {
????}
}
把self通過(guò)參數(shù)回傳給block,這樣就不會(huì)產(chǎn)生循環(huán)引用了。
block回傳的self可以聲明成id類型,這樣使用的時(shí)候可以在入?yún)⒙暶骶唧wself類型,避免顯式類型轉(zhuǎn)換,方便開發(fā)。
注意:block 內(nèi)部有延遲操作的時(shí)候,使用外部聲明的強(qiáng)引用

這時(shí)候你會(huì)發(fā)現(xiàn),在打印 block 內(nèi)容的之前,person 對(duì)象已經(jīng)被釋放了,自然,name 屬性的值,也是空的了。
另外也不只是after,asyn等都是這種情況。
此時(shí)需要使用
__strong ClassName *sself = wself;

總結(jié):存在使用了類的成員變量而導(dǎo)致循環(huán)引用的情況,需要使用__weak來(lái)弱引用。
????????????要判斷A是否引用B
4、CoreFoundation對(duì)象(C對(duì)象,或單獨(dú)與C混編) : 只要函數(shù)中包含了create\new\copy\retain等關(guān)鍵字, 那么這些方法產(chǎn)生的對(duì)象, 就必須在不再使用的時(shí)候調(diào)用1次CFRelease或者其他release函數(shù)
以上是內(nèi)存泄露的可能原因,但是如何檢測(cè)內(nèi)存泄露,并快速定位呢?
Instrument其實(shí)有很多不便
Leaks
先看看 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).
Abandoned memory: Memory still referenced by your application that has no useful purpose.
Cached memory: Memory still referenced by your application that might be used again for better performance.
1、 Leaked memory 和 Abandoned memory 都屬于應(yīng)該釋放而沒(méi)釋放的內(nèi)存,都是【內(nèi)存泄露】。
2、而 Leaks 工具只負(fù)責(zé)檢測(cè) Leaked memory,而不管 Abandoned memory。在 MRC 時(shí)代 Leaked memory 很常見(jiàn),因?yàn)楹苋菀淄苏{(diào)用 release,但在 ARC 時(shí)代更常見(jiàn)的內(nèi)存泄露是循環(huán)引用導(dǎo)致的 Abandoned memory,Leaks 工具查不出這類內(nèi)存泄露,應(yīng)用有限。
MLeaksFinder 框架
MLeaksFinder提供了內(nèi)存泄露檢測(cè)更好的解決方案。
1、只需要引入 MLeaksFinder,就可以自動(dòng)在 App 運(yùn)行過(guò)程檢測(cè)到內(nèi)存泄露的對(duì)象并立即提醒,
2、無(wú)需打開額外的工具。
3、也無(wú)需為了檢測(cè)內(nèi)存泄露而一個(gè)個(gè)場(chǎng)景去重復(fù)地操作。
總結(jié):MLeaksFinder 目前能自動(dòng)檢測(cè) UIViewController 和 UIView 對(duì)象的內(nèi)存泄露,而且也可以擴(kuò)展以檢測(cè)其它類型的對(duì)象。
MLeaksFinder 的使用很簡(jiǎn)單
參照https://github.com/Zepo/MLeaksFinder,基本上就是把 MLeaksFinder 目錄下的文件添加到你的項(xiàng)目中,就可以在運(yùn)行時(shí)(debug 模式下)幫助你檢測(cè)項(xiàng)目里的內(nèi)存泄露了,無(wú)需修改任何業(yè)務(wù)邏輯代碼,而且只在 debug 下開啟,完全不影響你的 release 包。
當(dāng)發(fā)生內(nèi)存泄露時(shí),MLeaksFinder 會(huì)中斷言,并準(zhǔn)確的告訴你哪個(gè)對(duì)象泄露了。這里設(shè)計(jì)為中斷言而不是打日志讓程序繼續(xù)跑,是因?yàn)楹芏嗳瞬粫?huì)去看日志,斷言則能強(qiáng)制開發(fā)者注意到并去修改,而不是犯拖延癥。
中斷言時(shí),控制臺(tái)會(huì)有如下提示,View-ViewController stack 從上往下看,該 stack 告訴你,MyTableViewController 的 UITableView 的 subview UITableViewWrapperView 的 subview MyTableViewCell 沒(méi)被釋放。而且,這里我們可以肯定的是 MyTableViewController,UITableView,UITableViewWrapperView 這三個(gè)已經(jīng)成功釋放了。

從 MLeaksFinder 的使用方法可以看出,MLeaksFinder 具備以下優(yōu)點(diǎn):
1、使用簡(jiǎn)單,不侵入業(yè)務(wù)邏輯代碼。
2、不用打開 Instrument不需要額外的操作,你只需開發(fā)你的業(yè)務(wù)邏輯,在你運(yùn)行調(diào)試時(shí)就能幫你檢測(cè)內(nèi)存泄露發(fā)現(xiàn)及時(shí),更改完代碼后一運(yùn)行即能發(fā)現(xiàn)(這點(diǎn)很重要,你馬上就能意識(shí)到哪里寫錯(cuò)了)精準(zhǔn),能準(zhǔn)確地告訴你哪個(gè)對(duì)象沒(méi)被釋放