<article style="box-shadow: rgba(0, 0, 0, 0.05) 0px 2px 4px 0px; background-color: rgb(255, 255, 255); padding: 20px 0px; color: rgb(0, 0, 0); font-family: "PingFang SC", "Microsoft YaHei", SimHei, Arial, SimSun; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">
最近,收到朋友求助读慎,說是有個控制器漱贱,一進(jìn)去就crash,而且手機(jī)非常的燙,用instrument跑了跑夭委,發(fā)現(xiàn)內(nèi)存暴增幾百兆幅狮;如圖:
圖中可以看出,內(nèi)存暴增的罪魁禍?zhǔn)资荵YImage株灸,再進(jìn)一步定位問題崇摄,如圖:
現(xiàn)在已經(jīng)可以很清楚的知道,具體是哪些代碼導(dǎo)致內(nèi)存飆升的慌烧,這個方法“YYCGImageCreateDecodeCopy”逐抑,主要是對圖像進(jìn)行解壓縮操作;同樣的屹蚊,換成SDWebImage厕氨,也出現(xiàn)了相同情況,由于某些原因淑翼,之后的分析都將以SDWebImage為例腐巢。
先貼上朋友調(diào)用的SDWebImage的代碼:
[self sd_setImageWithURL:[NSURL URLWithString:imageUrl] placeholderImage:[UIImage imageNamed:@"defaulImage"] options:SDWebImageProgressiveDownload completed:nil];
instrument分析圖:
代碼定位:
同樣的,也是在解壓縮的時候玄括,出現(xiàn)內(nèi)存飆升冯丙,但是為什么會這樣呢?
首先我想到“Create”必須得對應(yīng)一個“Release”,于是我認(rèn)真的看了每一行代碼胃惜,無論是YYImage還是SDWebImage泞莉,都嚴(yán)格遵守了這一準(zhǔn)則,既然都有Release船殉,那么就不是內(nèi)存泄露了鲫趁,應(yīng)該是在這幾行代碼執(zhí)行的過程中,產(chǎn)生的內(nèi)存消耗利虫“ず瘢可是怎么會消耗這么大呢?一張圖片也就幾M的大小啊糠惫,這個解壓縮存在的意義是什么呢疫剃?
我從“H_偉華 博樂家園”的一篇博客中找到了解壓縮存在的意義(http://blog.163.com/huang1988519@126/blog/static/737875752013101803137445/)
當(dāng)完成圖片加載或者從本地加載圖片時,還會有輕微的卡頓硼讽。
因為當(dāng)顯示或者繪制的時候巢价,UIKit 只做了額外的延遲初始化和消耗很高解碼。
而下面的代碼片段固阁,從后臺線程解壓縮成合適的格式壤躲,從而讓系統(tǒng)不必做額外的轉(zhuǎn)換。
然后在主線程上顯示
我又疑惑了备燃,既然是為了優(yōu)化碉克,為啥會適得其反呢?我百思不得其解赚爵,最后在SDWebImage的issues找到了相關(guān)的討論:
https://github.com/rs/SDWebImage/issues/538
其中一個harishkashyap大神是這么回答的:
harishkashyap commented on Dec 23, 2014
Its the memory issue again. decodedImageWithImage takes up huge memory and causes the app to crash. I have added an option to put this off in the library but defaulting to YES so there aren't any breaking changes. If you put off the decodeImageWithImage method in both image cache and image downloader then you shouldn't be seeing the VM: CG Raster data on the top consuming lots of memorydecodeImageWithImage is supposed to decompress images and cache them so the loading on tableviews/collectionviews become better. However, with large set of images being loaded, the experience worsened and the memory of uncompressed images even with thumbnails can consume GBs of memory. Putting this off only improved performance.
[[SDImageCache sharedImageCache] setShouldDecompressImages:NO]; [[SDWebImageDownloader sharedDownloader] setShouldDecompressImages:NO];
https://github.com/harishkashyap/SDWebImage/tree/fix-memory-issues
這位大神提到棉胀,decodeImageWithImage這個方法用于對圖片進(jìn)行解壓縮并且緩存起來,以保證tableviews/collectionviews 交互更加流暢冀膝,但是如果是加載高分辨率圖片的話唁奢,會適得其反,有可能造成上G的內(nèi)存消耗窝剖。該大神建議麻掸,對于高分辨率的圖片,應(yīng)該禁止解壓縮操作赐纱,相關(guān)的代碼處理為:
[[SDImageCache sharedImageCache] setShouldDecompressImages:NO];
[[SDWebImageDownloader sharedDownloader] setShouldDecompressImages:NO];
雖然這位大神給了肯定的答案脊奋,但是為什么會如此呢?
我查閱了apple官方文檔對CGBitmapContextCreate函數(shù)的注解:
圖中紅框部分的參數(shù)疙描,引起了我的注意:
bitsPerComponent 表示存入內(nèi)存中的每個像素中的每一個組件所占的位數(shù)诚隙;
bytesPerRow 表示存入內(nèi)存中的位圖的每一行所占的字節(jié)數(shù);
我猜測起胰,解壓縮操作中久又,每一個像素點都會分配一個空間來存儲相關(guān)值,那么分辨率越高的圖片,就意味著更多數(shù)量的像素點地消,也就意味著需要分配更多的空間炉峰!所以對于高分辨率圖來說,解壓縮操作的確會造成內(nèi)存飆升脉执,即使是幾M的圖片疼阔,解壓縮過程中也是有可能消耗上G的內(nèi)存!
既然如此半夷,我決定按照harishkashyap大神的方法婆廊,直接讓下載高分辨率圖的地方,禁止解壓縮操作玻熙!
朋友說否彩,高清圖涉及到的地方,都全部已經(jīng)封裝起來了嗦随,那么就輕松了很多。為了保證封裝類不對外界產(chǎn)生影響敬尺,我只在調(diào)用封裝類時枚尼,禁用解壓縮,調(diào)用完畢再恢復(fù)原設(shè)置即可砂吞。這樣既能保證高分辨率圖不crash署恍,也能保證其他地方,普通圖片依舊可以通過解壓縮進(jìn)行優(yōu)化蜻直。
朋友封裝的是一個控制器盯质,所以我決定在控制器loadView方法中禁用解壓縮,在delloc方法中恢復(fù)原設(shè)置:
1概而、首先在封裝的控制器中定義變量用于存儲原設(shè)置:
static BOOL SDImageCacheOldShouldDecompressImages = YES;
static BOOL SDImagedownloderOldShouldDecompressImages = YES;
2呼巷、loadView中保存原設(shè)置并且禁用解壓縮:
SDImageCache *canche = [SDImageCache sharedImageCache];
SDImageCacheOldShouldDecompressImages = canche.shouldDecompressImages;
canche.shouldDecompressImages = NO;
SDWebImageDownloader *downloder = [SDWebImageDownloader sharedDownloader];
SDImagedownloderOldShouldDecompressImages = downloder.shouldDecompressImages;
downloder.shouldDecompressImages = NO;
3、dell中恢復(fù)原設(shè)置:
-(void)dealloc {
SDImageCache *canche = [SDImageCache sharedImageCache];
canche.shouldDecompressImages = SDImageCacheOldShouldDecompressImages;
SDWebImageDownloader *downloder = [SDWebImageDownloader sharedDownloader];
downloder.shouldDecompressImages = SDImagedownloderOldShouldDecompressImages;
}
再次用instrument跑了一下赎瑰,方法果然有效王悍,內(nèi)存徹底降下來了,如圖:
當(dāng)然餐曼,如果你也可以設(shè)置SDWebImage的其他參數(shù)压储,比如是否緩存到內(nèi)存以及內(nèi)存緩存最高限制等,來保證內(nèi)存安全:
shouldCacheImagesInMemory 是否緩存到內(nèi)存
maxMemoryCost 內(nèi)存緩存最高限制
文章出自CSDN:http://blog.csdn.net/guojiezhi/article/details/52033796