- 內(nèi)存溢出 (out of memory):是指程序在申請(qǐng)內(nèi)存時(shí)溯街,沒有足夠的內(nèi)存空間供其使用祭犯,出現(xiàn)out of memory墓懂。通俗理解就是內(nèi)存不夠,通常在運(yùn)行大型軟件或游戲時(shí)七蜘,軟件或游戲所需要的內(nèi)存遠(yuǎn)遠(yuǎn)超出了你主機(jī)內(nèi)安裝的內(nèi)存所承受大小谭溉,就叫內(nèi)存溢出。
- 內(nèi)存泄露( memory leak):是指程序在申請(qǐng)內(nèi)存后橡卤,無法釋放已申請(qǐng)的內(nèi)存空間扮念,一次內(nèi)存泄露危害可以忽略,但內(nèi)存泄露堆積后果很嚴(yán)重碧库,無論多少內(nèi)存,遲早會(huì)被占光柜与。
一般易造成泄漏的點(diǎn):
- Retain Cycle,Block強(qiáng)引用
- NSTimer釋放不當(dāng)
- 第三方提供方法造成的內(nèi)存泄漏
- CoreFoundation方式申請(qǐng)的內(nèi)存谈为,忘記釋放
eg:
block循環(huán):
[cell setSelectTagCityBlock:^(NSIndexPath *indexPath, NSInteger index){
[self tableView:_tableViewCityList didSelectRowAtIndexPath:indexPath index:index];
}];
一般用weak打破保留環(huán)
@WeakObj(self)
[cell setSelectTagCityBlock:^(NSIndexPath *indexPath, NSInteger index){
if (selfWeak)
{
[selfWeak tableView:selfWeak.tableViewCityList didSelectRowAtIndexPath:indexPath index:index];
}
}];
AFNetWorking上的經(jīng)典代碼旅挤,防止循環(huán)引用的
//創(chuàng)建__weak弱引用,防止強(qiáng)引用互相持有
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
//創(chuàng)建局部__strong強(qiáng)引用伞鲫,防止多線程情況下weakSelf被析構(gòu)
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
weak 本身是可以避免循環(huán)引用的問題的粘茄,但是其會(huì)導(dǎo)致外部對(duì)象釋放了之后,block 內(nèi)部也訪問不到這個(gè)對(duì)象的問題,我們可以通過在 block 內(nèi)部聲明一個(gè) strong 的變量來指向 weakObj柒瓣,使外部對(duì)象既能在 block 內(nèi)部保持住儒搭,又能避免循環(huán)引用的問題
block 本身無法避免循環(huán)引用的問題,但是我們可以通過在 block 內(nèi)部手動(dòng)把 blockObj 賦值為 nil 的方式來避免循環(huán)引用的問題芙贫。另外一點(diǎn)就是 block 修飾的變量在 block 內(nèi)外都是唯一的搂鲫,要注意這個(gè)特性可能帶來的隱患。
_timer = [NSTimer timerWithTimeInterval:[refreshTime integerValue]
target:self
selector:@selector(doFSearchDoubleBackNumberRequest:)
userInfo:searchResult
repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
Timer 添加到 Runloop 的時(shí)候磺平,會(huì)被 Runloop 強(qiáng)引用魂仍。
Timer 又會(huì)有一個(gè)對(duì) Target 的強(qiáng)引用。
所以說如果不對(duì)Timer進(jìn)行釋放拣挪,Timer的targer(self)也一直不會(huì)被釋放擦酌。
有時(shí)候我們我們對(duì)某個(gè)Timer的targer設(shè)置了nil。但沒設(shè)置[timer invalidate]菠劝。
其實(shí)這個(gè)對(duì)象還是沒被釋放的赊舶。timer對(duì)應(yīng)的執(zhí)行方法也一直會(huì)在線程中執(zhí)行。容易造成內(nèi)存泄露赶诊。
注:repeats:NO不會(huì)強(qiáng)引用
一笼平、排查方法
我們知道,iOS開發(fā)中對(duì)內(nèi)存管理的要求非常嚴(yán)格舔痪,一旦存在內(nèi)存泄漏寓调,后果是非常嚴(yán)重的,會(huì)導(dǎo)致程序非常容易崩潰辙喂。盡管目前iOS開發(fā)基本上都是采用的ARC方式進(jìn)行內(nèi)存管理捶牢,但是一不小心就會(huì)存在內(nèi)存泄漏的問題鸠珠。
首先巍耗,我們需要定位內(nèi)存泄漏的問題,目前比較常用的內(nèi)存泄漏的排查方法有兩種渐排,都在xcode中可以直接使用:靜態(tài)分析方法(Analyze)和動(dòng)態(tài)分析方法(Instrument的leak)炬太。
1.1 靜態(tài)內(nèi)存泄漏分析方法 (command + shift + b)
主要分析以下四種問題:
1、邏輯錯(cuò)誤:訪問空指針或未初始化的變量等驯耻;
2亲族、內(nèi)存管理錯(cuò)誤:如內(nèi)存泄漏等;
3可缚、聲明錯(cuò)誤:從未使用過的變量霎迫;
4、Api調(diào)用錯(cuò)誤:未包含使用的庫和框架帘靡。
通過xcode打開項(xiàng)目知给,然后點(diǎn)擊product-->Analyze ,如下圖左側(cè)的圖所示,這樣就開始對(duì)項(xiàng)目進(jìn)行靜態(tài)內(nèi)存泄漏分析涩赢,分析結(jié)果如下圖右側(cè)的圖所示戈次。根據(jù)分析結(jié)果進(jìn)行休整之后在進(jìn)行分析就好了。
靜態(tài)分析方法能發(fā)現(xiàn)大部分的問題筒扒,但是只能是靜態(tài)分析結(jié)果怯邪,有一些并不準(zhǔn)確,還有一些動(dòng)態(tài)分配內(nèi)存的情形并沒有進(jìn)行分析花墩。所以僅僅使用靜態(tài)內(nèi)存泄漏分析得到的結(jié)果并不是非承可靠,如果需要冰蘑,我們需要將對(duì)項(xiàng)目進(jìn)行更為完善的內(nèi)存泄漏分析和排查搂捧。那就需要用到我們下面要介紹的動(dòng)態(tài)內(nèi)存泄漏分析方法Instruments中的Leaks方法進(jìn)行排查。
1.2 動(dòng)態(tài)內(nèi)存泄漏分析方法
分析內(nèi)存泄露不能把所有的內(nèi)存泄露查出來懂缕,有的內(nèi)存泄露是在運(yùn)行時(shí)允跑,用戶操作時(shí)才產(chǎn)生的。那就需要用到Instruments了搪柑。具體操作是通過xcode打開項(xiàng)目聋丝,然后點(diǎn)擊product-->profile,如下圖所示工碾。
按上面操作弱睦,build成功后跳出Instruments工具,如上圖右側(cè)圖所示渊额。選擇Leaks選項(xiàng)况木,點(diǎn)擊右下角的【choose】按鈕,這時(shí)候項(xiàng)目程序也在模擬器或手機(jī)上運(yùn)行起來了旬迹,在手機(jī)或模擬器上對(duì)程序進(jìn)行操作火惊,工具顯示效果如下:
點(diǎn)擊左上角的紅色圓點(diǎn),這時(shí)項(xiàng)目開始啟動(dòng)了奔垦,由于leaks是動(dòng)態(tài)監(jiān)測(cè)屹耐,所以手動(dòng)進(jìn)行一系列操作,可檢查項(xiàng)目中是否存在內(nèi)存泄漏問題椿猎。如圖所示惶岭,橙色矩形框中所示綠色為正常,如果出現(xiàn)如右側(cè)紅色矩形框中顯示紅色犯眠,則表示出現(xiàn)內(nèi)存泄漏按灶。
選中Leaks Checks,在Details所在欄中選擇CallTree,并且在右下角勾選Invert Call Tree 和Hide System Libraries,會(huì)發(fā)現(xiàn)顯示若干行代碼筐咧,雙擊即可跳轉(zhuǎn)到出現(xiàn)內(nèi)存泄漏的地方鸯旁,修改即可。
- Separate by Thread:按線程分開做分析,這樣更容易揪出那些吃資源的問題線程羡亩。特別是對(duì)于主線程摩疑,它要處理和渲染所有的接口數(shù)據(jù),一旦受到阻塞畏铆,程序必然卡頓或停止響應(yīng)雷袋。
- Invert Call Tree:反向輸出調(diào)用樹。把調(diào)用層級(jí)最深的方法顯示在最上面辞居,更容易找到最耗時(shí)的操作楷怒。
- Hide System Libraries:隱藏系統(tǒng)庫文件。過濾掉各種系統(tǒng)調(diào)用瓦灶,只顯示自己的代碼調(diào)用鸠删。
- Flattern Recursion:拼合遞歸。將同一遞歸函數(shù)產(chǎn)生的多條堆棧(因?yàn)檫f歸函數(shù)會(huì)調(diào)用自己)合并為一條
二贼陶、內(nèi)存泄漏的原因分析
在目前主要以ARC進(jìn)行內(nèi)存管理的開發(fā)模式刃泡,導(dǎo)致內(nèi)存泄漏的根本原因是代碼中存在循環(huán)引用,從而導(dǎo)致一些內(nèi)存無法釋放碉怔,這就會(huì)導(dǎo)致dealloc()方法無法被調(diào)用烘贴。主要原因大概有一下幾種類型。
2.1 ViewController中存在NSTimer
如果你的ViewController中有NSTimer撮胧,那么你就要注意了桨踪,因?yàn)楫?dāng)你調(diào)用
[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(updateTime:)
userInfo:nil
repeats:YES];
時(shí)的target:self
就增加了ViewController的return count
,如果你不將這個(gè)timer invalidate
芹啥,將別想調(diào)用dealloc
锻离。
2.2 ViewController中的代理delegate
一個(gè)比較隱秘的因素,你去找找與這個(gè)類有關(guān)的代理墓怀,有沒有強(qiáng)引用屬性汽纠?如果你這個(gè)VC需要外部傳某個(gè)Delegate進(jìn)來,來通過Delegate+protocol的方式傳參數(shù)給其他對(duì)象捺疼,那么這個(gè)delegate一定不要強(qiáng)引用疏虫,盡量assign
或者weak
,否則你的VC會(huì)持續(xù)持有這個(gè)delegate啤呼,直到它自身被釋放。
2.3 ViewController中Block
這個(gè)可能就是經(jīng)常容易犯的一個(gè)問題了呢袱,Block體內(nèi)使用實(shí)例變量也會(huì)造成循環(huán)引用官扣,使得擁有這個(gè)實(shí)例的對(duì)象不能釋放。因?yàn)樵揵lock本來就是當(dāng)前viewcontroller的一部分羞福,現(xiàn)在蓋子部門又強(qiáng)引用self惕蹄,導(dǎo)致循環(huán)引用無法釋放。 例如你這個(gè)類叫OneViewController,有個(gè)屬性是NSString *name; 如果你在block體中使用了self.name,或者_(dá)name卖陵,那樣子的話這個(gè)類就沒法釋放遭顶。 要解決這個(gè)問題其實(shí)很簡(jiǎn)單,就是在block之前申明當(dāng)前的self引用為弱引用
即可泪蔫。
//MRC下代碼如下
__block Viewcontroller *weakSelf = self;
//ARC下代碼如下
__weak Viewcontroller *weakSelf = self;
2.4 ViewController的子視圖對(duì)self的持有
這個(gè)問題也是我的項(xiàng)目中內(nèi)存泄漏的問題所在棒旗。我們有時(shí)候需要在子視圖或者某個(gè)cell中點(diǎn)擊跳轉(zhuǎn)等操作,需要在子視圖或cell中持有當(dāng)前的ViewController對(duì)象撩荣,這樣跳轉(zhuǎn)之后的back鍵才能直接返回該頁面铣揉,同時(shí)也不銷毀當(dāng)前ViewController。此時(shí)餐曹,你就要注意在子視圖或者cell中對(duì)當(dāng)前頁面的持有對(duì)象不能是強(qiáng)引用逛拱,盡量assign或者weak,否則會(huì)造成循環(huán)引用台猴,內(nèi)存無法釋放朽合。
問題1:
關(guān)于"User-facing text should use localized string macro”警告的解決
使用靜態(tài)分析,發(fā)現(xiàn)工程中有1000多個(gè)“User-facing text should use localized string macro”警告饱狂,如下圖:
給label賦值的時(shí)候旁舰,提示
面向用戶的文本應(yīng)該使用本地化的字符串宏
此為代碼中配置了本地化,面向用戶的應(yīng)該用字符串宏,而我們直接賦值為漢字.
解決方法:
問題2:
NSMutableArray *tempMutArr = [NSMutableArray arrayWithCapacity:0];
if ([self.clickedButtonTpye isEqualToString:KClickedButtonTypeLast]) {
tempMutArr = self.lastDataSourceArr;
}else{
tempMutArr = self.hotDataSourceArr;
}
用Analyze檢測(cè)后報(bào)出了下面的錯(cuò)誤
Value stored to 'dataArr' during its initialization is never read
仔細(xì)看了代碼后才發(fā)現(xiàn)代碼存在一個(gè)細(xì)節(jié)上的問題,也正是這個(gè)細(xì)節(jié)導(dǎo)致的內(nèi)存泄漏嗡官。在項(xiàng)目里我的self.lastDataSourceArr和self.hotDataSourceArr都是已經(jīng)初始化過的可變數(shù)組箭窜,但是在if里我又重新初始化了一個(gè)新的可變數(shù)組,并且把之前已經(jīng)初始化過的可變數(shù)組賦值給了它衍腥,這就是內(nèi)訓(xùn)泄漏的問題所在磺樱,看似代碼沒有大的問題,但是其實(shí)已經(jīng)造成了內(nèi)存泄漏婆咸。原因是:因?yàn)閟elf.lastDataSourceArr和self.hotDataSourceArr是已經(jīng)創(chuàng)建并且分配過內(nèi)存的可變數(shù)組了竹捉,
但是我把這些數(shù)組又賦值給了重新創(chuàng)建并分配了內(nèi)存的可變數(shù)組tempMutArr,所以這樣就出現(xiàn)了一個(gè)數(shù)據(jù)源卻申請(qǐng)了兩塊內(nèi)存的情況尚骄,那么就存在一塊內(nèi)存空閑了块差,所以就存在了內(nèi)存泄漏。
其實(shí)本意很簡(jiǎn)單倔丈,就是想把兩個(gè)可變數(shù)組分情況賦值給可變數(shù)組tempMutArr憨闰,那么我們完全可以這樣做:
NSMutableArray *tempMutArr;
if ([self.clickedButtonTpye isEqualToString:KClickedButtonTypeLast]) {
tempMutArr = self.lastDataSourceArr;
}else{
tempMutArr = self.hotDataSourceArr;
}
這樣只做了一個(gè)可變數(shù)組tempMutArr的聲明,不需要給它分配實(shí)際內(nèi)存需五,就可以賦值了鹉动,其實(shí)只是聲明了這樣一個(gè)對(duì)象,使其持有對(duì)原有數(shù)據(jù)對(duì)象的引用即可
文章來自轉(zhuǎn)發(fā):https://www.cnblogs.com/mukekeheart/p/8144742.html