iOS 異步加載本地圖片
問題
當(dāng)某個界面使用系統(tǒng)API + (nullable UIImage *)imageNamed:(NSString *)name;
加載了過多本地圖片資源時,不可避免的會產(chǎn)生卡頓感。
有過instrument
中Time Profiler
經(jīng)驗的同學(xué)應(yīng)該有數(shù)據(jù)上的直觀體驗长已,調(diào)用imageNamed:
首次加載某張圖片,往往是最耗時的乘陪。
- I/O操作:首次加載本地圖片時,內(nèi)存中是沒有的雕擂,因此去磁盤中加載圖片啡邑。
- 解碼:解碼圖片,將位圖數(shù)據(jù)還原成原始像素數(shù)據(jù)井赌。
以上兩步都是imageNamed:
方法在底層做的耗時操作谤逼。知道原因贵扰,那么方案也就有了,將以上操作放入到子線程中流部。
方案1
有些同學(xué)可能想戚绕,在子線程調(diào)用imageNamed:
先提前加載一次圖片,并且系統(tǒng)會幫我們緩存枝冀,省事省力省心舞丛,美滋滋~但是這里有兩個坑:
-
線程安全問題,該方法在iOS9以后才線程安全果漾,也就是說iOS9以下版本使用有風(fēng)險球切;
15486648779770.jpg 效率問題,將
imageNamed:
方法放入到子線程調(diào)用绒障,使用Time Profiler
觀察會發(fā)現(xiàn)吨凑,該方法在子線程耗時比在主線程久的多,猜測為了保證線程安全户辱,底層很多加鎖的操作導(dǎo)致鸵钝;
方案2
那么美滋滋的不行,只能繞一點遠(yuǎn)路了庐镐;
加載圖片
// I/O將磁盤數(shù)據(jù)加載入內(nèi)存中恩商,NSData類型
+ (nullable instancetype)dataWithContentsOfFile:(NSString *)path;
// 將NSData類型的圖片數(shù)據(jù)轉(zhuǎn)為UIImage類型
- (nullable instancetype)initWithData:(NSData *)data scale:(CGFloat)scale NS_AVAILABLE_IOS(6_0);
解碼圖片
站在前人肩膀上敲代碼很方便,如果你的項目中使用了SDWebImage或YYImage的話焚鹊,可以直接調(diào)用它們框架內(nèi)部的解碼圖片方法;
YYImageDecoder類
- (instancetype)imageByDecoded;
SDWebImageDecoder類
+ (UIImage *)decodedImageWithImage:(UIImage *)image;
它們內(nèi)部邏輯相似:獲取imageA
對象位圖的信息(透明通道韧献、縮放系數(shù)末患、寬高),創(chuàng)建渲染的上下文(CGBitmapContextCreate
)锤窑,將位圖渲染在上下文中(CGContextDrawImage
)璧针,在上下文中提取帶有位圖信息的imageB
對象(CGBitmapContextCreateImage
);
緩存
這樣子系統(tǒng)就不會幫我們緩存圖片了渊啰,因為需要我們自己管理緩存圖片探橱。
使用YYThreadSafeDictionary
線程安全的容器,子線程保存下解碼后的圖片绘证。
以上幾步操作都是在子線程中隧膏,降低了主線程的占用,可以有效的解決由于加載本地圖片造成的卡頓問題嚷那。當(dāng)在子線程處理完圖片后胞枕,我們再切換到主線程,將圖片扔給使用方魏宽。
// 調(diào)用接口大致這樣
+ (void)asyncLoadImgWithimgName:(NSString *)imgName block:(void(^)(UIImage *image))block;
Github代碼 FGAsyncLoadImgUtil
以上就是項目中使用的一個小優(yōu)化方案腐泻,如果同學(xué)們其他idea决乎,希望不吝賜教。