ios開發(fā)內(nèi)存管理(一)

1.最近學(xué)習(xí)了一下ios內(nèi)存管理方面的問題感覺這個整理的比較全面幕屹,大家可以相互交流一下:

在ObjC中,對象什么時候會被釋放(或者對象占用的內(nèi)存什么時候會被回收利用)勉抓?

答案是:當(dāng)對象沒有被任何變量引用(也可以說是沒有指針指向該對象)的時候,就會被釋放。

那怎么知道對象已經(jīng)沒有被引用了呢澎羞?

ObjC采用引用計數(shù)(reference counting)的技術(shù)來進行管理:

1)每個對象都有一個關(guān)聯(lián)的整數(shù),稱為引用計數(shù)器

2)當(dāng)代碼需要使用該對象時敛苇,則將對象的引用計數(shù)加1

3)當(dāng)代碼結(jié)束使用該對象時妆绞,則將對象的引用計數(shù)減1

4)當(dāng)引用計數(shù)的值變?yōu)?時,表示對象沒有被任何代碼使用枫攀,此時對象將被釋放括饶。

與之對應(yīng)的消息發(fā)送方法如下:

1)當(dāng)對象被創(chuàng)建(通過alloc、new或copy等方法)時来涨,其引用計數(shù)初始值為1

2)給對象發(fā)送retain消息图焰,其引用計數(shù)加1

3)給對象發(fā)送release消息,其引用計數(shù)減1

4)當(dāng)對象引用計數(shù)歸0時蹦掐,ObjC給對象發(fā)送dealloc消息銷毀對象

下面通過一個簡單的例子來說明:

場景:有一個寵物中心(內(nèi)存)技羔,可以派出小動物(對象)陪小朋友們玩耍(對象引用者),現(xiàn)在xiaoming想和小狗一起玩耍卧抗。

新建Dog類藤滥,重寫其創(chuàng)建和銷毀的方法:

@implementation?Dog

????-?(instancetype)init?{

????????if?(self?=?[super?init])?{

????????????NSLog(@"小狗被派出去啦社裆!初始引用計數(shù)為?%ld",self.retainCount);

????????}

????????return?self;

????}

????-?(void)dealloc?{

????????NSLog(@"小狗回到寵物中心");

????????[super?dealloc];

????}

@end

在main方法中創(chuàng)建dog對象拙绊,給dog發(fā)送消息

//模擬:寵物中心派出小狗

Dog?*?dog?=?[[Dog?alloc]init];

//模擬:xiaoming需要和小狗玩耍,需要將其引用計數(shù)加1

[dog?retain];

NSLog(@"小狗的引用計數(shù)為?%ld",dog.retainCount);

//模擬:xiaoming不和小狗玩耍了泳秀,需要將其引用計數(shù)減1

[dog?release];

NSLog(@"小狗的引用計數(shù)為?%ld",dog.retainCount);

//沒人需要和小狗玩耍了标沪,將其引用計數(shù)減1

[dog?release];

//將指針置nil,否則變?yōu)橐爸羔?/p>

dog?=?nil;

輸出結(jié)果為

[34691:7638855]?初始引用計數(shù)為?1

[34691:7638855]?小狗的引用計數(shù)為?2

[34691:7638855]?小狗的引用計數(shù)為?1

[34691:7638855]?銷毀Dog

可以看到嗜傅,引用計數(shù)幫助寵物中心很好的標記了小狗的使用狀態(tài)谨娜,在完成任務(wù)的時候及時收回到寵物中心。

思考幾個問題:

1)NSString引用計數(shù)問題

如果我們嘗試查看一個string的引用計數(shù)

NSString?*?str?=?@"hello?guys";

NSLog(@"%ld",?str.retainCount);

會發(fā)現(xiàn)引用計數(shù)為-1磺陡,這可以理解為NSString實際上是一個字符串常量趴梢,是沒有引用計數(shù)的(或者它的引用計數(shù)是一個很大的值(使用%lu可以打印查看),對它做引用計數(shù)操作沒實質(zhì)上的影響)币他。

2)賦值不會擁有某個對象

NSString?*?name?=?dog.name;

這里僅僅是指針賦值操作坞靶,并不會增加name的引用計數(shù),需要持有對象必須要發(fā)送retain消息蝴悉。

3)dealloc

由于釋放對象是會調(diào)用dealloc方法彰阴,因此重寫dealloc方法來查看對象釋放的情況,如果沒有調(diào)用則會造成內(nèi)存泄露拍冠。在上面的例子中我們通過重寫dealloc讓小狗被釋放的時候打印日志來告訴我們已經(jīng)完成釋放尿这。

4)在上面例子中簇抵,如果我們增加這樣一個操作

//沒人需要和小狗玩耍了,將其引用計數(shù)減1

[dog?release];

NSLog(@"%ld",dog.retainCount);

會發(fā)現(xiàn)獲取到的引用計數(shù)為1射众,為什么不是0呢碟摆?

這是因為對引用計數(shù)為1的對象release時,系統(tǒng)知道該對象將被回收叨橱,就不會再對該對象的引用計數(shù)進行減1操作典蜕,這樣可以增加對象回收的效率。

另外罗洗,對已釋放的對象發(fā)送消息是不可取的愉舔,因為對象的內(nèi)存已被回收,如果發(fā)送消息時伙菜,該內(nèi)存已經(jīng)被其他對象使用了轩缤,得到的結(jié)果是無法確定的,甚至?xí)斐杀罎ⅰ?/p>

2贩绕、自動釋放池

現(xiàn)在已經(jīng)明確了典奉,當(dāng)不再使用一個對象時應(yīng)該將其釋放,但是在某些情況下丧叽,我們很難理清一個對象什么時候不再使用(比如xiaoming和小狗玩耍結(jié)束的時間不確定)卫玖,這可怎么辦?

ObjC提供autorelease方法來解決這個問題踊淳,當(dāng)給一個對象發(fā)送autorelease消息時假瞬,方法會在未來某個時間給這個對象發(fā)送release消息將其釋放,在這個時間段內(nèi)迂尝,對象還是可以使用的脱茉。

那autorelease的原理是什么呢?

原理就是對象接收到autorelease消息時垄开,它會被添加到了當(dāng)前的自動釋放池中琴许,當(dāng)自動釋放池被銷毀時,會給池里所有的對象發(fā)送release消息溉躲。

這里就引出了自動釋放池這個概念榜田,什么是自動釋放池呢? 顧名思義锻梳,就是一個池箭券,這個池可以容納對象,而且可以自動釋放疑枯,這就大大增加了我們處理對象的靈活性辩块。

自動釋放池怎樣創(chuàng)建?

ObjC提供兩種方法創(chuàng)建自動釋放池:

方法一:使用NSAutoreleasePool來創(chuàng)建

NSAutoreleasePool?*?pool?=?[[NSAutoreleasePool?alloc]init];

//這里寫代碼

[pool?release];

方法二:使用@autoreleasepool創(chuàng)建

@autoreleasepool?{

????//這里寫代碼

}

自動釋放池創(chuàng)建后,就會成為活動的池子废亭,釋放池子后国章,池子將釋放其所包含的所有對象。

以上兩種方法推薦第一種豆村,因為將內(nèi)存交給ObjC管理更高效液兽。

自動釋放池什么時候創(chuàng)建?

app使用過程中你画,會定期自動生成和銷毀自動釋放池抵碟,一般是在程序事件處理之前創(chuàng)建桃漾,當(dāng)然我們也可以自行創(chuàng)建自動釋放池坏匪,來達到我們一些特定的目的。

自動釋放池什么時候銷毀撬统?

自動釋放池的銷毀時間是確定的适滓,一般是在程序事件處理之后釋放,或者由我們自己手動釋放恋追。

下面舉例說明自動釋放池的工作流程:

場景:現(xiàn)在xiaoming和xiaohong都想和小狗一起玩耍凭迹,但是他們的需求不一樣,他們的玩耍時間不一樣苦囱,流程如下

//創(chuàng)建一個自動釋放池

NSAutoreleasePool?*?pool?=?[[NSAutoreleasePool?alloc]?init];

//模擬:寵物中心派出小狗

Dog?*?dog?=?[[Dog?alloc]init];

//模擬:xiaoming需要和小狗玩耍嗅绸,需要將其引用計數(shù)加1

[dog?retain];

NSLog(@"小狗的引用計數(shù)為?%ld",dog.retainCount);

//模擬:xiaohong需要和小狗玩耍,需要將其引用計數(shù)加1

[dog?retain];

NSLog(@"小狗的引用計數(shù)為?%ld",dog.retainCount);

//模擬:xiaoming確定不想和小狗玩耍了撕彤,需要將其引用計數(shù)減1

[dog?release];

NSLog(@"小狗的引用計數(shù)為?%ld",dog.retainCount);

//模擬:xiaohong不確定何時不想和小狗玩耍了鱼鸠,將其設(shè)置為自動釋放

[dog?autorelease];

NSLog(@"小狗的引用計數(shù)為?%ld",dog.retainCount);

//沒人需要和小狗玩耍了,將其引用計數(shù)減1

[dog?release];

NSLog(@"釋放池子");

[pool?release];

//創(chuàng)建一個自動釋放池

@autoreleasepool?{

????//模擬:寵物中心派出小狗

????Dog?*?dog?=?[[Dog?alloc]init];

????//模擬:xiaoming需要和小狗玩耍羹铅,需要將其引用計數(shù)加1

????[dog?retain];

????NSLog(@"小狗的引用計數(shù)為?%ld",dog.retainCount);

????//模擬:xiaohong需要和小狗玩耍蚀狰,需要將其引用計數(shù)加1

????[dog?retain];

????NSLog(@"小狗的引用計數(shù)為?%ld",dog.retainCount);

????//模擬:xiaoming確定不想和小狗玩耍了,需要將其引用計數(shù)減1

????[dog?release];

????NSLog(@"小狗的引用計數(shù)為?%ld",dog.retainCount);

????//模擬:xiaohong不確定何時不想和小狗玩耍了职员,將其設(shè)置為自動釋放

????[dog?autorelease];

????NSLog(@"小狗的引用計數(shù)為?%ld",dog.retainCount);

????//沒人需要和小狗玩耍了麻蹋,將其引用計數(shù)減1

????[dog?release];

????NSLog(@"釋放池子");

}

輸出結(jié)果如下:

[34819:7801589]?初始引用計數(shù)為?1

[34819:7801589]?小狗的引用計數(shù)為?2

[34819:7801589]?小狗的引用計數(shù)為?3

[34819:7801589]?小狗的引用計數(shù)為?2

[34819:7801589]?小狗的引用計數(shù)為?2

[34819:7801589]?釋放池子

[34819:7801589]?銷毀Dog

可以看到,當(dāng)池子釋放后焊切,dog對象才被釋放扮授,因此在池子釋放之前,xiaohong都可以盡情地和小狗玩耍专肪。

使用自動釋放池需要注意:

1)自動釋放池實質(zhì)上只是在釋放的時候給池中所有對象對象發(fā)送release消息糙箍,不保證對象一定會銷毀,如果自動釋放池向?qū)ο蟀l(fā)送release消息后對象的引用計數(shù)仍大于1牵祟,對象就無法銷毀深夯。

2)自動釋放池中的對象會集中同一時間釋放,如果操作需要生成的對象較多占用內(nèi)存空間大,可以使用多個釋放池來進行優(yōu)化咕晋。比如在一個循環(huán)中需要創(chuàng)建大量的臨時變量雹拄,可以創(chuàng)建內(nèi)部的池子來降低內(nèi)存占用峰值。

3)autorelease不會改變對象的引用計數(shù)

自動釋放池的常見問題:

在管理對象釋放的問題上掌呜,自動幫助我們釋放池節(jié)省了大量的時間滓玖,但是有時候它卻未必會達到我們期望的效果,比如在一個循環(huán)事件中质蕉,如果循環(huán)次數(shù)較大或者事件處理占用內(nèi)存較大势篡,就會導(dǎo)致內(nèi)存占用不斷增長,可能會導(dǎo)致不希望看到的后果模暗。

示例代碼:


for(int?i?=?0;?i?<?100000;?i?++)?{

????NSString?*?log??=?[NSString?stringWithFormat:@"%d",?i];

????NSLog(@"%@",?log);

}

前面講過禁悠,自動釋放池的釋放時間是確定的,這個例子中自動釋放池會在循環(huán)事件結(jié)束時釋放兑宇,那問題來了:在這個十萬次的循環(huán)中碍侦,每次都會生成一個字符串并打印,這些字符串對象都放在池子中并直到循環(huán)結(jié)束才會釋放隶糕,因此在循環(huán)期間內(nèi)存不增長瓷产。

這類問題的解決方案是在循環(huán)中創(chuàng)建新的自動釋放池,多少個循環(huán)釋放一次由我們自行決定枚驻。

for(int?i?=?0;?i?<?100000;?i?++)?{

????@autoreleasepool?{

????????NSString?*?log??=?[NSString?stringWithFormat:@"%d",?i];

????????NSLog(@"%@",?log);

????}

}

3濒旦、iOS的內(nèi)存管理規(guī)則

3.1 基本原則

無規(guī)矩不成方圓池磁,在iOS開發(fā)中也存在規(guī)則來約束開發(fā)者進行內(nèi)存管理穆咐,總的來講有三點:

1)當(dāng)你通過new、alloc或copy方法創(chuàng)建一個對象時照激,它的引用計數(shù)為1霎冯,當(dāng)不再使用該對象時铃拇,應(yīng)該向?qū)ο蟀l(fā)送release或者autorelease消息釋放對象。

2)當(dāng)你通過其他方法獲得一個對象時沈撞,如果對象引用計數(shù)為1且被設(shè)置為autorelease慷荔,則不需要執(zhí)行任何釋放對象的操作;

3)如果你打算取得對象所有權(quán)缠俺,就需要保留對象并在操作完成之后釋放显晶,且必須保證retain和release的次數(shù)對等。

應(yīng)用到文章開頭的例子中壹士,小朋友每申請一個小狗(生成對象)磷雇,最后都要歸還到寵物中心(釋放對象),如果只申請而不歸還(對象創(chuàng)建了沒有釋放)躏救,那寵物中心的小狗就會越來越少(可用內(nèi)存越來越少)唯笙,到最后一個小狗都沒有了(內(nèi)存被耗盡)螟蒸,其他小朋友就再也沒有小狗可申請了(無資源可申請使用),因此崩掘,必須要遵守規(guī)則:申請必須歸還(規(guī)則1)七嫌,申請幾個必須歸還幾個(規(guī)則3),如果小狗被設(shè)定歸還時間則不用小朋友主動歸還(規(guī)則2)苞慢。

有興趣的讀者可以思考:

以上原則可以總結(jié)成一句簡潔的話诵原,是什么呢?

3.2 ARC

在MRC時代挽放,必須嚴格遵守以上規(guī)則绍赛,否則內(nèi)存問題將成為惡魔一樣的存在,然而來到ARC時代辑畦,事情似乎變得輕松了吗蚌,不用再寫無止盡的ratain和release似乎讓開發(fā)變得輕松了,對初學(xué)者變得更友好航闺。

ObjC2.0引入了垃圾回收機制褪测,然而由于垃圾回收機制會對移動設(shè)備產(chǎn)生某些不好的影響(例如由于垃圾清理造成的卡頓)猴誊,iOS并不支持這個機制潦刃,蘋果的解決方案就是ARC(自動引用計數(shù))。

iOS5以后懈叹,我們可以開啟ARC模式乖杠,ARC可以理解成一位管家,這個管家會幫我們向?qū)ο蟀l(fā)送retain和release語句澄成,不再需要我們手動添加了胧洒,我們可以更舒心地創(chuàng)建或引用對象,簡化內(nèi)存管理步驟墨状,節(jié)省大量的開發(fā)時間卫漫。

實際上,ARC不是垃圾回收肾砂,也并不是不需要內(nèi)存管理了列赎,它是隱式的內(nèi)存管理,編譯器在編譯的時候會在代碼插入合適的ratain和release語句镐确,相當(dāng)于在背后幫我們完成了內(nèi)存管理的工作包吝。

下面將自動釋放池的例子轉(zhuǎn)化成ARC來看看


@autoreleasepool?{

????Dog?*?dog?=?[[Dog?alloc]init];

????[xiaoming?playWithDog:dog];

????[xiaohong?playWithDog:dog];

????NSLog(@"釋放池子");

}

怎么樣,是不是簡潔了很多源葫,是不是很熟悉的感覺呢诗越。

注意:

1)如果你的工程歷史比較久,可以將其從MRC轉(zhuǎn)換成ARC息堂,跟上時代的步伐更好地維護

2)如果你的工程引用了某些不支持ARC的庫嚷狞,可以在Build Phases的Compile Sources將對應(yīng)的m文件的編譯器參數(shù)配置為-fno-objc-arc

3)ARC能幫我們簡化內(nèi)存管理問題,但不代表它是萬能的,還是有它不能處理的情況床未,這就需要我們自己手動處理褂乍,比如循環(huán)引用、非ObjC對象即硼、Core Foundation中的malloc()或者free()等等

有興趣的讀者可以思考:

MRC有什么缺點逃片?ARC有什么局限性?請列舉只酥。

3.3 ARC的修飾符

ARC提供四種修飾符褥实,分別是strong, weak, autoreleasing, unsafe_unretained。

__strong:強引用裂允,持有所指向?qū)ο蟮乃袡?quán)损离,無修飾符情況下的默認值。如需強制釋放绝编,可置nil僻澎。

比如我們常用的定時器

1NSTimer?*?timer?=?[NSTimer?timerWith...];

相當(dāng)于

1NSTimer?*?__strong?timer?=?[NSTimer?timerWith...];

當(dāng)不需要使用時,強制銷毀定時器


[timer?invalidate];

timer?=?nil;

__weak:弱引用十饥,不持有所指向?qū)ο蟮乃袡?quán)窟勃,引用指向的對象內(nèi)存被回收之后,引用本身會置nil逗堵,避免野指針秉氧。

比如避免循環(huán)引用的弱引用聲明:

1__weak?__typeof(self)?weakSelf?=?self;

__autoreleasing:自動釋放對象的引用,一般用于傳遞參數(shù)

比如一個讀取數(shù)據(jù)的方法

1-?(void)loadData:(NSError?**)error;

當(dāng)你調(diào)用時會發(fā)現(xiàn)這樣的提示


NSError?*?error;

[dataTool?loadData:(NSError?*__autoreleasing?*)]

這是編譯器自動幫我們插入以下代碼


NSError?*?error;

NSError?*?__autoreleasing?tmpErr?=?error;

[dataTool?loadData:&tmpErr];

__unsafe_unretained:為兼容iOS5以下版本的產(chǎn)物蜒秤,可以理解成MRC下的weak汁咏,現(xiàn)在基本用不到,這里不作描述作媚。

有興趣的讀者可以思考:

1)__strong NSTimer * timer 和 NSTimer * __strong timer哪個寫法是正確的, 為什么編譯器不報錯攘滩?

2)使用__autoreleasing可能會遇到哪些問題?

3.4 屬性的內(nèi)存管理

ObjC2.0引入了@property纸泡,提供成員變量訪問方法漂问、權(quán)限、環(huán)境弟灼、內(nèi)存管理類型的聲明级解,下面主要說明ARC中屬性的內(nèi)存管理。

屬性的參數(shù)分為三類田绑,基本數(shù)據(jù)類型默認為(atomic,readwrite,assign)勤哗,對象類型默認為(atomic,readwrite,strong),其中第三個參數(shù)就是該屬性的內(nèi)存管理方式修飾掩驱,修飾詞可以是以下之一:

1)assign:直接賦值

assign一般用來修飾基本數(shù)據(jù)類型

@property (nonatomic, assign) NSInteger count;

當(dāng)然也可以修飾ObjC對象芒划,但是不推薦冬竟,因為被assign修飾的對象釋放后,指針還是指向釋放前的內(nèi)存民逼,在后續(xù)操作中可能會導(dǎo)致內(nèi)存問題引發(fā)崩潰泵殴。

2)retain:release舊值,再retain新值(引用計數(shù)+1)

retain和strong一樣拼苍,都用來修飾ObjC對象笑诅。

使用set方法賦值時,實質(zhì)上是會先保留新值疮鲫,再釋放舊值吆你,再設(shè)置新值,避免新舊值一樣時導(dǎo)致對象被釋放的的問題俊犯。

MRC寫法如下

-?(void)setCount:(NSObject?*)count?{

????[count?retain];

????[_count?release];

????_count?=?count;

}

ARC對應(yīng)寫法


-?(void)setCount:(NSObject?*)count?{

????_count?=?count;

}

3)copy:release舊值妇多,再copy新值(拷貝內(nèi)容)

一般用來修飾String、Dict燕侠、Array等需要保護其封裝性的對象者祖,尤其是在其內(nèi)容可變的情況下,因此會拷貝(深拷貝)一份內(nèi)容給屬性使用绢彤,避免可能造成的對源內(nèi)容進行改動七问。

使用set方法賦值時,實質(zhì)上是會先拷貝新值杖虾,再釋放舊值烂瘫,再設(shè)置新值媒熊。

實際上奇适,遵守NSCopying的對象都可以使用copy,當(dāng)然芦鳍,如果你確定是要共用同一份可變內(nèi)容嚷往,你也可以使用strong或retain。

@property (nonatomic, copy) NSString * name;

4)weak:ARC新引入修飾詞柠衅,可代替assign皮仁,比assign多增加一個特性(置nil,見上文)菲宴。

weak和strong一樣用來修飾ObjC對象贷祈。

使用set方法賦值時,實質(zhì)上不保留新值喝峦,也不釋放舊值势誊,只設(shè)置新值。

比如常用的代理的聲明

1@property?(weak)?id<mydelegate>?delegate;</mydelegate>

Xib控件的引用

1@property?(weak,?nonatomic)?IBOutlet?UIImageView?*productImage;

5)strong:ARC新引入修飾詞谣蠢,可代替retain

可參照retain粟耻,這里不再作描述查近。

有興趣的讀者可以思考:

1)各個屬性修飾詞和3.3中的修飾詞的對應(yīng)關(guān)系?

2)屬性的本質(zhì)是什么挤忙?

3.5 block的內(nèi)存管理

iOS中使用block必須自己管理內(nèi)存霜威,錯誤的內(nèi)存管理將導(dǎo)致循環(huán)引用等內(nèi)存泄漏問題,這里主要說明在ARC下block聲明和使用的時候需要注意的兩點:

1)如果你使用@property去聲明一個block的時候册烈,一般使用copy來進行修飾(當(dāng)然也可以不寫戈泼,編譯器自動進行copy操作),盡量不要使用retain赏僧。

@property (nonatomic, copy) void(^block)(NSData * data);

2)block會對內(nèi)部使用的對象進行強引用矮冬,因此在使用的時候應(yīng)該確定不會引起循環(huán)引用,當(dāng)然保險的做法就是添加弱引用標記次哈。

__weak typeof(self) weakSelf = self;

有興趣的讀者可以深入了解:

1胎署、block的內(nèi)部實現(xiàn)原理是什么?

2窑滞、從內(nèi)存位置來看block有幾種類型琼牧?它們的內(nèi)存管理方式各是怎樣的?

3哀卫、對于不同類型的外部變量巨坊,block的內(nèi)存管理都是怎樣的?

4 經(jīng)典內(nèi)存泄漏及其解決方案

雖然ARC好處多多此改,然而也并無法避免內(nèi)存泄漏問題趾撵,下面介紹在ARC中常見的內(nèi)存泄漏。

4.1 僵尸對象和野指針

僵尸對象:內(nèi)存已經(jīng)被回收的對象共啃。

野指針:指向僵尸對象的指針占调,向野指針發(fā)送消息會導(dǎo)致崩潰。

野指針錯誤形式在Xcode中通常表現(xiàn)為:Thread 1:EXC_BAD_ACCESS移剪,因為你訪問了一塊已經(jīng)不屬于你的內(nèi)存究珊。

例子代碼:(沒有出現(xiàn)錯誤的筒子多運行幾遍,因為獲取野指針指向的結(jié)果是不確定的)


Dog?*?dog?=?[[Dog?alloc]init];

NSLog(@"before");

NSLog(@"%s",object_getClassName(dog));

[dog?release];

NSLog(@"after");

NSLog(@"%s",object_getClassName(dog));

運行結(jié)果:


[15184:5811062]?before

[15184:5811062]?Dog

[15184:5811062]?after

(lldb)

可以看到纵苛,當(dāng)運行到第六行的時候崩潰了剿涮,并給出了EXC_BAD_ACCESS的提示。

解決方案:

對象已經(jīng)被釋放后攻人,應(yīng)將其指針置為空指針(沒有指向任何對象的指針取试,給空指針發(fā)送消息不會報錯)。

然而在實際開發(fā)中實際遇到EXC_BAD_ACCESS錯誤時怀吻,往往很難定位到錯誤點瞬浓,幸好Xcode提供方便的工具給我們來定位及分析錯誤。

1)在product-scheme-edit scheme-diagnostics中將enable zombie objects勾選上烙博,下次再出現(xiàn)這樣的錯誤就可以準確定位了瑟蜈。

運行結(jié)果:


[15169:5801945]?before

[15169:5801945]?Dog

[15169:5801945]?after

[15169:5801945]?_NSZombie_Dog

可以看到烟逊,當(dāng)運行到第六行時并沒有崩潰,并給出了NSZombie的提示铺根。

2)在Xcode-open developer tool-Instruments打開工具集宪躯,選擇Zombies工具可以對已安裝的應(yīng)用進行僵尸對象檢測。

4.2 循環(huán)引用

循環(huán)引用是ARC中最常出現(xiàn)的問題位迂,對于可能引發(fā)循環(huán)引用的一些原因在前一篇文章iOS總結(jié)篇:影響控制器正常釋放的常見問題中有提及访雪,大家可以看看。

一般來講循環(huán)引用也是可以使用工具來檢測到的掂林,分為兩種:

1)在product-Analyze中使用靜態(tài)分析來檢測代碼中可能存在循環(huán)引用的問題臣缀。

2)在Xcode-open developer tool-Instruments打開工具集,選擇Leaks工具可以對已安裝的應(yīng)用進行內(nèi)存泄漏檢測泻帮,此工具能檢測靜態(tài)分析不會提示精置,但是到運行時才會出現(xiàn)的內(nèi)存泄漏問題。

Leaks工具雖然強大锣杂,但是它不能檢測到block循環(huán)引用導(dǎo)致的內(nèi)存泄漏脂倦,這種情況一般需要自行排查問題(考驗?zāi)愕幕竟r候到了),傻瓜式的方案當(dāng)然是重寫對象的dealloc方法來監(jiān)測對象是否正常釋放元莫,來確認沒有形成循環(huán)引用赖阻。

由于ARC中循環(huán)引用出現(xiàn)的幾率相對較大,很多大神或者團隊都提供了很多解決此問題的思路和方法踱蠢,甚至開發(fā)了插件和類庫來幫助開發(fā)者更好地檢測問題火欧,有興趣的讀者可以研究一下,是否好用茎截,孰好孰壞就由讀者自行評判了苇侵。

4.3 循環(huán)中對象占用內(nèi)存大

這個問題常見于循環(huán)次數(shù)較大,循環(huán)體生成的對象占用內(nèi)存較大的情景稼虎。

例子代碼:我需要10000個演員來打仗


for(int?i?=?0;?i?<?10000;?i?++)?{

????Person?*?soldier?=?[[Person?alloc]init];

????[soldier?fight];

}

該循環(huán)內(nèi)產(chǎn)生大量的臨時對象衅檀,直至循環(huán)結(jié)束才釋放,可能導(dǎo)致內(nèi)存泄漏霎俩,解決方法和上文中提到的自動釋放池常見問題類似:在循環(huán)中創(chuàng)建自己的autoReleasePool,及時釋放占用內(nèi)存大的臨時變量沉眶,減少內(nèi)存占用峰值打却。

for(int?i?=?0;?i?<?10000;?i?++)?{

????@autoreleasepool?{

????Person?*?soldier?=?[[Person?alloc]init];

????[soldier?fight];

????}

}

然而有時候autoReleasePool也不是萬能的:

例子:假如有2000張圖片,每張1M左右谎倔,現(xiàn)在需要獲取所有圖片的尺寸柳击,你會怎么做?

如果這樣做


for(int?i?=?0;?i?<?2000;?i?++)?{

????CGSize?size?=?[UIImage?imageNamed:[NSString?stringWithFormat:@"%d.jpg",i]].size;

????//add?size?to?array

}

用imageNamed方法加載圖片占用Cache的內(nèi)存片习,autoReleasePool也不能釋放捌肴,對此問題需要另外的解決方法蹬叭,當(dāng)然保險的當(dāng)然是雙管齊下了


for(int?i?=?0;?i?<?2000;?i?++)?{

????????@autoreleasepool?{

????????CGSize?size?=?[UIImage?imageWithContentsOfFile:filePath].size;

????????//add?siez?to?array

????}

}

4.4 無限循環(huán)

這個是比4.3更極端的情況,無論你出于什么原因状知,當(dāng)你啟動了一個無限循環(huán)的時候秽五,ARC會默認該方法用不會執(zhí)行完畢,方法里面的對象就永不釋放饥悴,內(nèi)存無限上漲坦喘,導(dǎo)致內(nèi)存泄漏。

例子:

NSLog(@"start?!");

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{

????????BOOL?isSucc?=?YES;

????????while(isSucc)?{

????????[NSThread?sleepForTimeInterval:1.0];

????????NSLog(@"create?an?obj");

????}

});

輸出結(jié)果為

[7026:3555827]?start?!

[7026:3556236]?create?an?obj

[7026:3556236]?create?an?obj

[7026:3556236]?create?an?obj

[7026:3556236]?create?an?obj

[7026:3555827]?dealloc

[7026:3556236]?create?an?obj

[7026:3556236]?create?an?obj

[7026:3556236]?create?an?obj

可以看到西设,當(dāng)控制器釋放后該循環(huán)還在繼續(xù)瓣铣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市贷揽,隨后出現(xiàn)的幾起案子棠笑,更是在濱河造成了極大的恐慌,老刑警劉巖禽绪,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腐晾,死亡現(xiàn)場離奇詭異,居然都是意外死亡丐一,警方通過查閱死者的電腦和手機藻糖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來库车,“玉大人巨柒,你說我怎么就攤上這事∧埽” “怎么了洋满?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長珍坊。 經(jīng)常有香客問我牺勾,道長,這世上最難降的妖魔是什么阵漏? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任驻民,我火速辦了婚禮,結(jié)果婚禮上履怯,老公的妹妹穿的比我還像新娘回还。我一直安慰自己,他們只是感情好叹洲,可當(dāng)我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布柠硕。 她就那樣靜靜地躺著,像睡著了一般运提。 火紅的嫁衣襯著肌膚如雪蝗柔。 梳的紋絲不亂的頭發(fā)上闻葵,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音癣丧,去河邊找鬼槽畔。 笑死,一個胖子當(dāng)著我的面吹牛坎缭,可吹牛的內(nèi)容都是我干的竟痰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼掏呼,長吁一口氣:“原來是場噩夢啊……” “哼坏快!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起憎夷,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤莽鸿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后拾给,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體祥得,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年蒋得,在試婚紗的時候發(fā)現(xiàn)自己被綠了级及。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡额衙,死狀恐怖饮焦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情窍侧,我是刑警寧澤县踢,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站伟件,受9級特大地震影響硼啤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜斧账,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一谴返、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧其骄,春花似錦亏镰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钧忽。三九已至毯炮,卻和暖如春逼肯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背桃煎。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工篮幢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人为迈。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓三椿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親葫辐。 傳聞我的和親對象是個殘疾皇子搜锰,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,685評論 2 360

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

  • 在此特此聲明:一下所有鏈接均來自互聯(lián)網(wǎng),在此記錄下我的查閱學(xué)習(xí)歷程耿战,感謝各位原創(chuàng)作者的無私奉獻 蛋叼! 技術(shù)一點一點積...
    遠航的移動開發(fā)歷程閱讀 11,145評論 12 197
  • 播客節(jié)目《新聞酸菜館》最新一期的話題是吐槽《舌尖上的中國2》。主持們說剂陡,《舌尖1》是新的嘗試狈涮,沒有被過分干擾,講的...
    努力攢錢的二花閱讀 1,082評論 3 5
  • 感覺2017年剛開始就是拼命地活著鸭栖。 12號剛帶完輔導(dǎo)班歌馍,匆匆忙忙和曦曦吃個飯,收拾到13號凌晨5點晕鹊,稀稀拉拉的東...
    白藜蘆醇閱讀 206評論 3 1
  • 首先男女主人公貝兒和王子一個貌美一個帥氣顏值高松却。 其次城堡建筑風(fēng)格華美高貴,貝兒居住的鄉(xiāng)村生活氣息濃厚 再次影片先...
    乘格帆閱讀 451評論 0 2
  • 脂類是人體需要的重要營養(yǎng)素之一捏题,它與蛋白質(zhì)玻褪、碳水化合物是產(chǎn)能的三大營養(yǎng)素,在供給人體能量方面起著重要作用公荧。 ...
    約梵閱讀 947評論 0 0