iOS內(nèi)存泄漏

  • 內(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)行分析就好了。


memory1.png

靜態(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,如下圖所示工碾。


memory2.png

按上面操作弱睦,build成功后跳出Instruments工具,如上圖右側(cè)圖所示渊额。選擇Leaks選項(xiàng)况木,點(diǎn)擊右下角的【choose】按鈕,這時(shí)候項(xiàng)目程序也在模擬器或手機(jī)上運(yùn)行起來了旬迹,在手機(jī)或模擬器上對(duì)程序進(jìn)行操作火惊,工具顯示效果如下:


memory3.png

點(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)存泄漏按灶。


memory4.png

選中Leaks Checks,在Details所在欄中選擇CallTree,并且在右下角勾選Invert Call Tree 和Hide System Libraries,會(huì)發(fā)現(xiàn)顯示若干行代碼筐咧,雙擊即可跳轉(zhuǎn)到出現(xiàn)內(nèi)存泄漏的地方鸯旁,修改即可。


memory5.png
  • 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”警告饱狂,如下圖:

靜態(tài)分析-1.png

給label賦值的時(shí)候旁舰,提示
面向用戶的文本應(yīng)該使用本地化的字符串宏
此為代碼中配置了本地化,面向用戶的應(yīng)該用字符串宏,而我們直接賦值為漢字.
靜態(tài)分析-2.png

解決方法:
靜態(tài)分析-3.png

問題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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宏邮,一起剝皮案震驚了整個(gè)濱河市泽示,隨后出現(xiàn)的幾起案子缸血,更是在濱河造成了極大的恐慌,老刑警劉巖械筛,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捎泻,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡埋哟,警方通過查閱死者的電腦和手機(jī)笆豁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來定欧,“玉大人渔呵,你說我怎么就攤上這事】仇” “怎么了扩氢?”我有些...
    開封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長爷辱。 經(jīng)常有香客問我录豺,道長,這世上最難降的妖魔是什么饭弓? 我笑而不...
    開封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任双饥,我火速辦了婚禮,結(jié)果婚禮上弟断,老公的妹妹穿的比我還像新娘咏花。我一直安慰自己,他們只是感情好阀趴,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開白布昏翰。 她就那樣靜靜地躺著,像睡著了一般刘急。 火紅的嫁衣襯著肌膚如雪棚菊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天叔汁,我揣著相機(jī)與錄音统求,去河邊找鬼。 笑死据块,一個(gè)胖子當(dāng)著我的面吹牛码邻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瑰钮,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼冒滩,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了浪谴?” 一聲冷哼從身側(cè)響起开睡,我...
    開封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎苟耻,沒想到半個(gè)月后篇恒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凶杖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年胁艰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片智蝠。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡腾么,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出杈湾,到底是詐尸還是另有隱情解虱,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布漆撞,位于F島的核電站殴泰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏浮驳。R本人自食惡果不足惜悍汛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望至会。 院中可真熱鬧离咐,春花似錦、人聲如沸奉件。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓶蚂。三九已至糖埋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間窃这,已是汗流浹背瞳别。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留杭攻,地道東北人祟敛。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像兆解,于是被迫代替她去往敵國和親馆铁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354