在實(shí)際項(xiàng)目中畜吊,用戶在上傳圖片時(shí),有時(shí)會(huì)一次性上傳大量的圖片即供。在上傳圖片前定拟,我們要進(jìn)行一系列操作,比如:旋轉(zhuǎn)圖片為正確方向逗嫡,壓縮圖片等青自,這些操作需要將圖片加載到內(nèi)存中,這時(shí)候會(huì)遇到的一個(gè)問題是內(nèi)存劇增驱证,導(dǎo)致內(nèi)存不夠用延窜,從而出現(xiàn)閃退的問題,下面對(duì)內(nèi)存的使用做詳細(xì)分析.
一抹锄、內(nèi)存分析逆瑞,非優(yōu)化
我在測試項(xiàng)目中,重復(fù)加載了一張圖片1000次伙单,首先加載圖片到內(nèi)存获高,然后進(jìn)行壓縮操作,釋放內(nèi)存
for (int i = 0; i <= 1000; i ++) {
//1.首先我們獲取到需要處理的圖片資源的路徑
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"upload" ofType:@"PNG"];
//2.將圖片加載到內(nèi)存中吻育,我們使用了alloc關(guān)鍵字念秧,在使用完后,可以手動(dòng)快速釋放掉內(nèi)存
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];
//3.這一步我們將圖片進(jìn)行了壓縮布疼,并得到一個(gè)autorelease類型實(shí)例
UIImage *image2 = [image imageByScalingAndCroppingForSize:CGSizeMake(480, 320)];
//4.釋放掉2步驟的內(nèi)存
[image release];
}
上面的代碼看起來沒有任何問題摊趾,可以說是一種標(biāo)準(zhǔn)的代碼寫法币狠,在每一步驟中都對(duì)內(nèi)存做了小心的處理,我們來看一下砾层,實(shí)際的內(nèi)存使用情況:
在上圖中可以看到漩绵,我們的操作在沒有任何問題的情況下,在加載大量圖片時(shí)肛炮,還是會(huì)造成內(nèi)存的劇增
可以看到自動(dòng)釋放內(nèi)存時(shí)止吐,圖片占用的內(nèi)存并沒有立即釋放掉
[圖片上傳失敗...(image-98bb33-1513839329366)]
在上圖中可以看到,我們的操作在沒有任何問題的情況下铸董,在加載大量圖片時(shí)祟印,還是會(huì)造成內(nèi)存的劇增
[圖片上傳失敗...(image-701454-1513839329365)]
可以看到自動(dòng)釋放內(nèi)存時(shí),圖片占用的內(nèi)存并沒有立即釋放掉
[圖片上傳失敗...(image-9f26ae-1513839329364)]
這些資源沒有立即釋放的資源粟害,占用了寶貴的內(nèi)存資源,最終使程序被kill
三優(yōu)化后的內(nèi)存使用
上面程序被kill颤芬,是因?yàn)槌绦虻膬?nèi)存使用問題悲幅,在上面的代碼中,我們每一步都對(duì)內(nèi)存做了非常小心的處理站蝠,但是在加載大量的圖片時(shí)汰具,還是會(huì)出現(xiàn)問題。其根本原因就是autorelease惹的禍菱魔,autorelease自動(dòng)釋放內(nèi)存留荔,并不會(huì)立即把內(nèi)存釋放掉,而是要等到下一個(gè)事件周期才會(huì)釋放掉澜倦。問題是一些資源我們不得不使用autorelease類型聚蝶,比如作為函數(shù)的返回值,而且系統(tǒng)api及項(xiàng)目是的大部分也都是這么做的藻治,如果全都依靠我們手動(dòng)釋放很容易造成內(nèi)存泄漏碘勉。
記住:NSAutoreleasePool里面的維護(hù)了一個(gè)NSMutableArray數(shù)組桩卵,所有標(biāo)記為autorelease的對(duì)象都會(huì)被添加都該數(shù)組中验靡。只有當(dāng)pool對(duì)象被drain的時(shí)候,才會(huì)去遍歷該數(shù)組雏节,若retainCount為0則釋放內(nèi)存胜嗓,不為零就發(fā)生內(nèi)存泄露!OC已經(jīng)為我們建立一個(gè)pool對(duì)象钩乍,但是該pool對(duì)象需要比較久的時(shí)間才能drain掉辞州,因此在一些遍歷處理的場景中,需要我們手動(dòng)去建立pool對(duì)象件蚕,并手動(dòng)drain掉孙技。
for (int i = 0; i <= 1000; i ++) {
//創(chuàng)建一個(gè)自動(dòng)釋放池
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"PNG"];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];
UIImage *image2 = [image imageByScalingAndCroppingForSize:CGSizeMake(480, 320)];
[image release];
//將自動(dòng)釋放池內(nèi)存釋放产禾,它會(huì)同時(shí)釋放掉上面代碼中產(chǎn)生的臨時(shí)變量image2
[pool drain];
}
優(yōu)化后的,內(nèi)存使用情況
[圖片上傳失敗...(image-35425-1513839372453)]
可用內(nèi)存不再劇減
CGImage及UIImage的數(shù)據(jù)由原來的220多減少到6-7個(gè)
可以看到使用了 NSAutoreleasePool后牵啦,加載大量圖片的時(shí)候內(nèi)存也不會(huì)出現(xiàn)問題
四亚情、自動(dòng)釋放池概述
(1)自動(dòng)釋放池被置于一個(gè)堆棧中,雖然它們通常被稱為被“嵌套”的哈雏。當(dāng)您創(chuàng)建一個(gè)新的自動(dòng)釋放池時(shí)楞件,它被添加到堆棧的頂部。當(dāng)自動(dòng)釋放池被回收時(shí)裳瘪,它們從堆棧中被刪除土浸。當(dāng)一個(gè)對(duì)象收到送autorelease消息時(shí),它被添加到當(dāng)前線程的目前處于棧頂?shù)淖詣?dòng)釋放池中彭羹。你不能向自動(dòng)釋放池發(fā)送autorelease或retain消息黄伊。Application Kit會(huì)在一個(gè)事件周期(或事件循環(huán)迭代)的開端—比如鼠標(biāo)按下事件—自動(dòng)創(chuàng)建一個(gè)自動(dòng)釋放池,并且在事件周期的結(jié)尾釋放它派殷,因此您的代碼通常不必關(guān)心还最。 有三種情況您應(yīng)該使用您自己的自動(dòng)釋放池:
如果您正在編寫一個(gè)不是基于Application Kit的程序,比如命令行工具毡惜,則沒有對(duì)自動(dòng)釋放池的內(nèi)置支持拓轻;您必須自己創(chuàng)建它們。
如果您生成了一個(gè)從屬線程经伙,則一旦該線程開始執(zhí)行扶叉,您必須立即創(chuàng)建您自己的自動(dòng)釋放池;否則帕膜,您將會(huì)泄漏對(duì)象枣氧。
如果您編寫了一個(gè)循環(huán),其中創(chuàng)建了許多臨時(shí)對(duì)象泳叠,您可以在循環(huán)內(nèi)部創(chuàng)建一個(gè)自動(dòng)釋放池作瞄,以便在下次迭代之前銷毀這些
對(duì)象。這可以幫助減少應(yīng)用程序的最大內(nèi)存占用量危纫。
(2) release和drain之間的差異
在引用計(jì)數(shù)環(huán)境下,release和drain一樣宗挥,會(huì)直接自動(dòng)釋放池l對(duì)象。
在GC(垃圾回收)環(huán)境下种蝶,release是一個(gè)no-op(空操作)契耿,drain會(huì)觸發(fā)垃圾回收(如果自上次垃圾回收以來分配的內(nèi)存大于當(dāng)前的閾值)。
通常情況下螃征,您都應(yīng)該使用drain而不是使用release來銷毀自動(dòng)釋放池搪桂。
-drain方法只適用于Mac OS X10.4(Tiger)及更高版本。
PS:如果項(xiàng)目使用的是ARC機(jī)制的,則在相同的位置使用@autoreleasepool {},
即踢械,
for (int i = 0; i <= 1000; i ++) {
//創(chuàng)建一個(gè)自動(dòng)釋放池
@autoreleasepool {
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"PNG"];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];
UIImage *image2 = [image imageByScalingAndCroppingForSize:CGSizeMake(480, 320)];
}
}
在實(shí)際項(xiàng)目中酗电,用戶在上傳圖片時(shí),有時(shí)會(huì)一次性上傳大量的圖片内列。在上傳圖片前撵术,我們要進(jìn)行一系列操作,比如:旋轉(zhuǎn)圖片為正確方向话瞧,壓縮圖片等嫩与,這些操作需要將圖片加載到內(nèi)存中,這時(shí)候會(huì)遇到的一個(gè)問題是內(nèi)存劇增交排,導(dǎo)致內(nèi)存不夠用划滋,從而出現(xiàn)閃退的問題,下面對(duì)內(nèi)存的使用做詳細(xì)分析.
一埃篓、內(nèi)存分析处坪,非優(yōu)化
我在測試項(xiàng)目中,重復(fù)加載了一張圖片1000次都许,首先加載圖片到內(nèi)存稻薇,然后進(jìn)行壓縮操作,釋放內(nèi)存
for (int i = 0; i <= 1000; i ++) {
//1.
首先我們獲取到需要處理的圖片資源的路徑
NSString *filePath = [[NSBundle mainBundle]pathForResource:@"upload"ofType:@"PNG"];
//2.
將圖片加載到內(nèi)存中胶征,我們使用了
alloc
關(guān)鍵字,在使用完后桨仿,可以手動(dòng)快速釋放掉內(nèi)存
UIImage *image = [[UIImage alloc]initWithContentsOfFile:filePath];
//3.
這一步我們將圖片進(jìn)行了壓縮睛低,并得到一個(gè)
autorelease
類型實(shí)例
UIImage *image2 = [image imageByScalingAndCroppingForSize:CGSizeMake(480, 320)];
//4.
釋放掉
2步驟的內(nèi)存
[image release];
}
上面的代碼看起來沒有任何問題,可以說是一種標(biāo)準(zhǔn)的代碼寫法服傍,在每一步驟中都對(duì)內(nèi)存做了小心的處理钱雷,我們來看一下,實(shí)際的內(nèi)存使用情況:
[圖片上傳失敗...(image-87484c-1513838509643)]
在上圖中可以看到吹零,我們的操作在沒有任何問題的情況下罩抗,在加載大量圖片時(shí),還是會(huì)造成內(nèi)存的劇增
[圖片上傳失敗...(image-837e1a-1513838509638)]
可以看到自動(dòng)釋放內(nèi)存時(shí)灿椅,圖片占用的內(nèi)存并沒有立即釋放掉
[圖片上傳失敗...(image-9d2b2b-1513838509636)]
這些資源沒有立即釋放的資源套蒂,占用了寶貴的內(nèi)存資源,最終使程序被kill
三優(yōu)化后的內(nèi)存使用
上面程序被kill茫蛹,是因?yàn)槌绦虻膬?nèi)存使用問題操刀,在上面的代碼中,我們每一步都對(duì)內(nèi)存做了非常小心的處理婴洼,但是在加載大量的圖片時(shí)骨坑,還是會(huì)出現(xiàn)問題。其根本原因就是
autorelease
惹的禍柬采,
autorelease自動(dòng)釋放內(nèi)存欢唾,并不會(huì)立即把內(nèi)存釋放掉且警,
而是要等到下一個(gè)事件周期才會(huì)釋放掉
。問題是一些資源我們不得不使用autorelease類型礁遣,比如作為函數(shù)的返回值斑芜,而且系統(tǒng)api及項(xiàng)目是的大部分也都是這么做的,如果全都依靠我們手動(dòng)釋放很容易造成內(nèi)存泄漏亡脸。
記籽禾隆:NSAutoreleasePool里面的維護(hù)了一個(gè)NSMutableArray數(shù)組,所有標(biāo)記為autorelease的對(duì)象都會(huì)被添加都該數(shù)組中浅碾。
只有當(dāng)pool對(duì)象被drain的時(shí)候大州,
才會(huì)去遍歷該數(shù)組,若retainCount為0則釋放內(nèi)存垂谢,不為零就發(fā)生內(nèi)存泄露厦画!OC已經(jīng)為我們建立一個(gè)pool對(duì)象,但是該pool對(duì)象需要比較久的時(shí)間才能drain掉滥朱,因此在一些遍歷處理的場景中根暑,需要我們手動(dòng)去建立pool對(duì)象,并手動(dòng)drain掉徙邻。
for (int i = 0; i <= 1000; i ++) {
//
創(chuàng)建一個(gè)自動(dòng)釋放池
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSString*filePath = [[NSBundle mainBundle]pathForResource:@"test"ofType:@"PNG"];
UIImage*image = [[UIImage alloc] initWithContentsOfFile:filePath];
UIImage *image2 = [image imageByScalingAndCroppingForSize:CGSizeMake(480, 320)];
[image release];
//
將自動(dòng)釋放池內(nèi)存釋放排嫌,它會(huì)同時(shí)釋放掉上面代碼中產(chǎn)生的臨時(shí)變量
image2 [pool drain];
}
優(yōu)化后的,內(nèi)存使用情況
[圖片上傳失敗...(image-169621-1513838509642)]
可用內(nèi)存不再劇減
CGImage及UIImage的數(shù)據(jù)由原來的220多減少到6-7個(gè)
可以看到使用了
NSAutoreleasePool
后缰犁,加載大量圖片的時(shí)候內(nèi)存也不會(huì)出現(xiàn)問題
四淳地、自動(dòng)釋放池概述
(1)
自動(dòng)釋放池被置于一個(gè)堆棧中,雖然它們通常被稱為被“嵌套”的帅容。當(dāng)您創(chuàng)建一個(gè)新的自動(dòng)釋放池時(shí)颇象,它被添加到堆棧的頂部。當(dāng)自動(dòng)釋放池被回收時(shí)并徘,它們從堆棧中被刪除遣钳。當(dāng)一個(gè)對(duì)象收到送autorelease消息時(shí),它被添加到當(dāng)前線程的目前處于棧頂?shù)淖詣?dòng)釋放池中麦乞。
你不能向
自動(dòng)釋放池發(fā)送autorelease或retain消息
蕴茴。
Application Kit會(huì)在一個(gè)事件周期(或事件循環(huán)迭代)的開端—比如鼠標(biāo)按下事件—自動(dòng)創(chuàng)建一個(gè)自動(dòng)釋放池,并且在事件周期的結(jié)尾釋放它路幸,因此您的代碼通常不必關(guān)心荐开。
有三種情況您應(yīng)該使用您自己的自動(dòng)釋放池:
如果您正在編寫一個(gè)不是基于Application Kit的程序,比如命令行工具简肴,則沒有對(duì)自動(dòng)釋放池的內(nèi)置支持晃听;您必須自己創(chuàng)建它們。
如果您生成了一個(gè)從屬線程,則一旦該線程開始執(zhí)行能扒,您必須立即創(chuàng)建您自己的自動(dòng)釋放池佣渴;否則,您將會(huì)泄漏對(duì)象初斑。
-
如果您編寫了一個(gè)循環(huán)辛润,其中創(chuàng)建了許多臨時(shí)對(duì)象,您可以在循環(huán)內(nèi)部創(chuàng)建一個(gè)自動(dòng)釋放池见秤,以便在下次迭代之前銷毀這些
對(duì)象砂竖。這可以幫助減少應(yīng)用程序的最大內(nèi)存占用量。
(2)
release和drain之間的差異
在引用計(jì)數(shù)環(huán)境下,release和drain一樣鹃答,會(huì)直接自動(dòng)釋放池l對(duì)象乎澄。
在GC(垃圾回收)環(huán)境下,release是一個(gè)no-op(空操作)测摔,drain會(huì)觸發(fā)垃圾回收(如果自上次垃圾回收以來分配的內(nèi)存大于當(dāng)前的閾值)置济。
通常情況下,您都應(yīng)該使用drain而不是使用release來銷毀自動(dòng)釋放池锋八。
-drain方法只適用于Mac OS X10.4(Tiger)及更高版本浙于。
PS:如果項(xiàng)目使用的是ARC機(jī)制的,則在相同的位置使用
@autoreleasepool{},
即挟纱,
//
創(chuàng)建一個(gè)自動(dòng)釋放池
@autoreleasepool
{
NSString*filePath = [[NSBundle mainBundle]pathForResource:@"test"ofType:@"PNG"];
UIImage *image = [[UIImage alloc]initWithContentsOfFile:filePath];
UIImage*image2 = [image imageByScalingAndCroppingForSize:CGSizeMake(480, 320)];
}
}