關于SDWebImage中的SDWebImageDecoder這個類,看完以后有下面幾個思考
0.為什么圖片要解碼
1.這么解碼的好處
2.這么解碼有壞處嗎
圖片的格式(png, jpg, gif ,webp,bitMap等等)
jpeg :
(1)有損壓縮格式, 將像素信息用jpeg保存成文件再讀取出來,其中某些像素值會有少許變化每瞒。
(2)沒有透明信息
(3)jpeg比較適合用來存儲相機拍攝出來的圖片
png :
(1) png是一種無損壓縮格式
(2) png可以有透明效果
(3)png比較適合矢量圖,幾何圖.
(矢量格式的一大優(yōu)點就是縮放.矢量格式的圖像其實是一組繪圖命令.這些指令通常是獨立于尺寸的.如果你想要擴大一個圓形,只需在繪制之前擴大他的半徑就可以了)
bitMap(位圖):
(1)bmp格式沒有壓縮像素格式
(2)存儲在文件中時先有文件頭月趟、再圖像頭济榨、后面就都是像素數據了搅吁,上下顛倒存儲
最早接觸到bitMap是在imageView的layer.shouldRasterize,你是否也用過這個屬性
CALayer的shouldRasterize和離屏渲染(offscreen rendering):
開啟shouldRasterize后,CALayer會被光柵化為bitmap,layer的陰影等效果也會被保存到bitmap中.
更新已光柵化的layer,會造成大量的offscreen渲染.
offscreen rendring: 指的是在圖像在繪制到當前屏幕前,需要先進行一次渲染,之后才繪制到當前屏幕。
離屏渲染有兩種:
1 .GPU的離屏渲染(shouldRasterize,mask等layer相關的 )
2 ,CPU的離屏渲染(使用drawRect,和CoreGraphic(線程安全,SDWebImage的解壓圖片))
?離屏渲染消耗性能:(看了好多文章都是這么寫的):
原因是顯卡需要另外alloc一塊內存來進行渲染,渲染完畢后在繪制到當前屏幕,而且對于顯卡來說,onscreen到offscreen的上下文環(huán)境切換是非常昂貴的(涉及到OpenGL的pipelines和barrier等).
開啟柵格化,相當于把部分GPU的工作轉到CPU來做了,由CPU生成bitMap,供GPU下次使用.
總結: 開起柵格化的圖片的內容不應該經常變化,否則造成大量的離屏渲染.它使用的范圍應該是當前l(fā)ayer的顯示比較復雜,但卻不怎么變化.
就可以開啟柵格化,在內存中保留一份,以方便使用.
Advanced Graphics and Animations for iOS Apps
這篇文章比較詳細,也介紹了Instruments對性能的測試,對上面有興趣的可以深看一下.
圖片的在內存中的大小
圖片在內存中占用的大小 跟圖片自身的大小沒有關系
內存中占用的大小 = 圖片的寬度 * 圖片的高度*每個像素占用的字節(jié)數
在SDWebImage中衡量大圖的標準:
圖片的像素總數 > 60M內存所存儲的像素數 ? 壓縮 : 不壓縮.
這個里面的大圖處理邏輯個SDWebImage 是完全一樣的,介紹更詳細,效果看起來也更好,建議看蘋果官方的.
基本原理:也就是原圖按照定好的大小(像素).來對原圖進行切塊,然后再一塊塊的繪制到destContext.
(這里網上Google了好多文章,對這個介紹很少,還是不怎么明白
static const CGFloat ?kDestSeemOverlap =2.0f;// the numbers of pixels to overlap(重疊) the seems where tiles(切片) meet.
為什么要定義這個,這個為什么會有重合的像素,以及它在后面的代碼里面的操作.還望有大神明白指導一下,不勝感激).
這些都是經過壓縮編碼后的圖片格式,是不能直接使用的,其基本原理
圖片為什么要解碼
png,jpeg格式的數據是不能直接使用的,需要將其轉化為位圖.
當我們使用imageView顯示圖片的時候:
1.讀取圖片
2.解壓圖片為位圖(消耗CPU)
3.如果位圖數據不是字節(jié)對齊的煤蚌,CoreAnimation會copy一份位圖數據并進行字節(jié)對齊
4 CoreAnimation渲染解壓縮過的位圖
這一切在IOS中都是默認發(fā)生在主線程成的并且是在UIImageView執(zhí)行setImage方法的時候完成的(在UIImage imageNamed的時候不會發(fā)生,具體參考下文).
當你使用[UIImage ImageNamed: @"xxx.jpg"]的時候,內存不會有明顯變化,只有當調用
UIImageView的setImage的時候,這時候內存才會增加.并且這時候你將imageView移除,內存也不會有減少. 這也是為什么
+ (nullableUIImage*)imageNamed:(NSString*)name;
不適合加載大的 不常用的圖片.因為它會默認在程序里保存這張圖片數據(不會隨ImageView的移除而移除).只有經常使用圖片適合這種方式加載.
+ (nullableUIImage*)imageWithContentsOfFile:(NSString*)path;
這個方法跟上面的略有不同,他不會在內存中保留一份數據.只要imageView移除,內存中的數據就會直接移除.這也就是這個方法為什么適合加載大的圖片,但卻不常用的圖片.
這樣解碼的好處:
把圖片解碼這個默認在主線程執(zhí)行,耗損CPU的行為,放在了后臺線程.
只需要在使用的時候,直接setImage,不會有太大的CPU消耗
這只是對圖片優(yōu)化的其中一種方式.在圖片從下載到顯示的過程中有很多個步驟可以優(yōu)化:
有興趣的可以參考這篇文章:
Instruments的測試
由于數據比較多,直接做成了gif動畫. 細細觀察吧
PS:
下面的前兩幅圖是執(zhí)行下面這段代碼:(圖1,圖2,內存和CPU的消耗):?
(1) 解壓是代碼執(zhí)行SDWebImage的:
NSString*path = [[NSBundlemainBundle]pathForResource:@"xxx.jpg"ofType:nil];
UIImage *image = [UIImage imageWithContentsOfFile:path];
_decodeImage = [UIImage decodedImageWithImage :image];(SDWebImage的decodedImageWithImage)方法
(2) 點擊灰色區(qū)域:
_showImageView.image = _decodeImage;
(3)點擊移除
[_showImageView removeFromSuperView];
_decodeImage = nil;
下面的后兩幅圖是執(zhí)行下面這段代碼:(圖3,圖4,內存和CPU的消耗):
(1) 點擊 使用UIImage imageName加載的按鈕 是代碼執(zhí)行的:
_image = [UIImage imageNamed:@"xxx.jpg"];
(2) 點擊灰色區(qū)域:
_showImageView.image = _image;
(3)點擊移除
[_showImageView removeFromSuperView];
_image = nil;
使用SDWebImage的decodedImageWithImage方法解碼的時候,內存的占用情況
使用SDWebImage的decodedImageWithImage方法解碼的時候,CPU的占用情況
使用[UIImage imageNamed:@"xxx.jpg"]的內存占用情況
使用[UIImage imageNamed:@"xxx.jpg"]的CPU占用情況
這樣解碼的弊端:
這樣解碼就是以空間換時間的方法,提前解壓好,用的時候直接從內存讀取.
如果下載的圖片比較大,然后直接解碼的話 這個是內存所不能承受,需要對圖片進行壓縮.
不過SDWebImage里面也提供了對大圖的壓縮API.