關(guān)于SDWebImage中的SDWebImageDecoder這個類,看完以后有下面幾個思考
0.為什么圖片要解碼
1.這么解碼的好處
2.這么解碼有壞處嗎
圖片的格式(png, jpg, gif ,webp,bitMap等等)
jpeg :
(1)有損壓縮格式, 將像素信息用jpeg保存成文件再讀取出來,其中某些像素值會有少許變化。
(2)沒有透明信息
(3)jpeg比較適合用來存儲相機(jī)拍攝出來的圖片
png :
(1) png是一種無損壓縮格式
(2) png可以有透明效果
(3)png比較適合矢量圖,幾何圖.
(矢量格式的一大優(yōu)點(diǎn)就是縮放.矢量格式的圖像其實是一組繪圖命令.這些指令通常是獨(dú)立于尺寸的.如果你想要擴(kuò)大一個圓形,只需在繪制之前擴(kuò)大他的半徑就可以了)
bitMap(位圖):
(1)bmp格式?jīng)]有壓縮像素格式
(2)存儲在文件中時先有文件頭、再圖像頭、后面就都是像素數(shù)據(jù)了,上下顛倒存儲
最早接觸到bitMap是在imageView的layer.shouldRasterize,你是否也用過這個屬性
CALayer的shouldRasterize和離屏渲染(offscreen rendering):
開啟shouldRasterize后,CALayer會被光柵化為bitmap,layer的陰影等效果也會被保存到bitmap中.
更新已光柵化的layer,會造成大量的offscreen渲染.
offscreen rendring: 指的是在圖像在繪制到當(dāng)前屏幕前,需要先進(jìn)行一次渲染,之后才繪制到當(dāng)前屏幕。
離屏渲染有兩種:
1 .GPU的離屏渲染(shouldRasterize,mask等layer相關(guān)的 )
2 ,CPU的離屏渲染(使用drawRect,和CoreGraphic(線程安全,SDWebImage的解壓圖片))
?離屏渲染消耗性能:(看了好多文章都是這么寫的):
原因是顯卡需要另外alloc一塊內(nèi)存來進(jìn)行渲染,渲染完畢后在繪制到當(dāng)前屏幕,而且對于顯卡來說,onscreen到offscreen的上下文環(huán)境切換是非常昂貴的(涉及到OpenGL的pipelines和barrier等).
開啟柵格化,相當(dāng)于把部分GPU的工作轉(zhuǎn)到CPU來做了,由CPU生成bitMap,供GPU下次使用.
總結(jié): 開起柵格化的圖片的內(nèi)容不應(yīng)該經(jīng)常變化,否則造成大量的離屏渲染.它使用的范圍應(yīng)該是當(dāng)前l(fā)ayer的顯示比較復(fù)雜,但卻不怎么變化.
就可以開啟柵格化,在內(nèi)存中保留一份,以方便使用.
Advanced Graphics and Animations for iOS Apps
這篇文章比較詳細(xì),也介紹了Instruments對性能的測試,對上面有興趣的可以深看一下.
圖片的在內(nèi)存中的大小
圖片在內(nèi)存中占用的大小 跟圖片自身的大小沒有關(guān)系
內(nèi)存中占用的大小 = 圖片的寬度 * 圖片的高度*每個像素占用的字節(jié)數(shù)
在SDWebImage中衡量大圖的標(biāo)準(zhǔn):
圖片的像素總數(shù) > 60M內(nèi)存所存儲的像素數(shù) ? 壓縮 : 不壓縮.
這個里面的大圖處理邏輯個SDWebImage 是完全一樣的,介紹更詳細(xì),效果看起來也更好,建議看蘋果官方的.
基本原理:也就是原圖按照定好的大小(像素).來對原圖進(jìn)行切塊,然后再一塊塊的繪制到destContext.
(這里網(wǎng)上Google了好多文章,對這個介紹很少,還是不怎么明白
static const CGFloat ?kDestSeemOverlap =2.0f;// the numbers of pixels to overlap(重疊) the seems where tiles(切片) meet.
為什么要定義這個,這個為什么會有重合的像素,以及它在后面的代碼里面的操作.還望有大神明白指導(dǎo)一下,不勝感激).
這些都是經(jīng)過壓縮編碼后的圖片格式,是不能直接使用的,其基本原理

圖片為什么要解碼
png,jpeg格式的數(shù)據(jù)是不能直接使用的,需要將其轉(zhuǎn)化為位圖.
當(dāng)我們使用imageView顯示圖片的時候:
1.讀取圖片
2.解壓圖片為位圖(消耗CPU)
3.如果位圖數(shù)據(jù)不是字節(jié)對齊的,CoreAnimation會copy一份位圖數(shù)據(jù)并進(jìn)行字節(jié)對齊
4 CoreAnimation渲染解壓縮過的位圖
這一切在IOS中都是默認(rèn)發(fā)生在主線程成的并且是在UIImageView執(zhí)行setImage方法的時候完成的(在UIImage imageNamed的時候不會發(fā)生,具體參考下文).
當(dāng)你使用[UIImage ImageNamed: @"xxx.jpg"]的時候,內(nèi)存不會有明顯變化,只有當(dāng)調(diào)用
UIImageView的setImage的時候,這時候內(nèi)存才會增加.并且這時候你將imageView移除,內(nèi)存也不會有減少. 這也是為什么
+ (nullableUIImage*)imageNamed:(NSString*)name;
不適合加載大的 不常用的圖片.因為它會默認(rèn)在程序里保存這張圖片數(shù)據(jù)(不會隨ImageView的移除而移除).只有經(jīng)常使用圖片適合這種方式加載.
+ (nullableUIImage*)imageWithContentsOfFile:(NSString*)path;
這個方法跟上面的略有不同,他不會在內(nèi)存中保留一份數(shù)據(jù).只要imageView移除,內(nèi)存中的數(shù)據(jù)就會直接移除.這也就是這個方法為什么適合加載大的圖片,但卻不常用的圖片.
這樣解碼的好處:
把圖片解碼這個默認(rèn)在主線程執(zhí)行,耗損CPU的行為,放在了后臺線程.
只需要在使用的時候,直接setImage,不會有太大的CPU消耗
這只是對圖片優(yōu)化的其中一種方式.在圖片從下載到顯示的過程中有很多個步驟可以優(yōu)化:
有興趣的可以參考這篇文章:
Instruments的測試
由于數(shù)據(jù)比較多,直接做成了gif動畫. 細(xì)細(xì)觀察吧
PS:
下面的前兩幅圖是執(zhí)行下面這段代碼:(圖1,圖2,內(nèi)存和CPU的消耗):?
(1) 解壓是代碼執(zhí)行SDWebImage的:
NSString*path = [[NSBundlemainBundle]pathForResource:@"xxx.jpg"ofType:nil];
UIImage *image = [UIImage imageWithContentsOfFile:path];
_decodeImage = [UIImage decodedImageWithImage :image];(SDWebImage的decodedImageWithImage)方法
(2) 點(diǎn)擊灰色區(qū)域:
_showImageView.image = _decodeImage;
(3)點(diǎn)擊移除
[_showImageView removeFromSuperView];
_decodeImage = nil;
下面的后兩幅圖是執(zhí)行下面這段代碼:(圖3,圖4,內(nèi)存和CPU的消耗):
(1) 點(diǎn)擊 使用UIImage imageName加載的按鈕 是代碼執(zhí)行的:
_image = [UIImage imageNamed:@"xxx.jpg"];
(2) 點(diǎn)擊灰色區(qū)域:
_showImageView.image = _image;
(3)點(diǎn)擊移除
[_showImageView removeFromSuperView];
_image = nil;
使用SDWebImage的decodedImageWithImage方法解碼的時候,內(nèi)存的占用情況

使用SDWebImage的decodedImageWithImage方法解碼的時候,CPU的占用情況

使用[UIImage imageNamed:@"xxx.jpg"]的內(nèi)存占用情況

使用[UIImage imageNamed:@"xxx.jpg"]的CPU占用情況

這樣解碼的弊端:
這樣解碼就是以空間換時間的方法,提前解壓好,用的時候直接從內(nèi)存讀取.
如果下載的圖片比較大,然后直接解碼的話 這個是內(nèi)存所不能承受,需要對圖片進(jìn)行壓縮.
不過SDWebImage里面也提供了對大圖的壓縮API.