前言
為什么需要對圖片進行解碼操作?
事實上,不管是 JPEG 還是 PNG 圖片也祠,都是一種壓縮的位圖圖形格式钾挟。只不過 PNG 圖片是無損壓縮男杈,并且支持 alpha 通道,而 JPEG 圖片則是有損壓縮,可以指定 0-100% 的壓縮比,因此,在將磁盤中的圖片渲染到屏幕之前,必須先要得到圖片的原始像素數(shù)據(jù)跟磨,才能執(zhí)行后續(xù)的繪制操作,這就是為什么需要對圖片解壓縮的原因攒盈。詳見 談談 iOS 中圖片的解壓縮 IOS 中圖片格式問題與性能優(yōu)化 iOS開發(fā):圖片格式與性能優(yōu)化
1.圖片解碼到底有多卡?
測試方法比較簡單抵拘,在一個可以tableView里面展示圖片,圖片是已經(jīng)放在本地的10張圖片,每張圖片大于1MB
代碼如下:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
BannerTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BannerTableViewCell" forIndexPath:indexPath];
// 獲取圖片
NSInteger index = 0;
index = indexPath.row%10;
NSString *imageName = [NSString stringWithFormat:@"backImage%ld",(long)index];
//UIImage *image = [UIImage imageNamed:imageName];
NSString *path = [[NSBundle mainBundle] pathForResource:imageName ofType:@"jpg"];
UIImage *image = [UIImage imageWithContentsOfFile:path];
cell.contentImageView.image = image;
return cell;
}
細心的同學可能已經(jīng)注意到了我在代碼中寫了兩種方式加載圖片。
一種是: [UIImage imageNamed:imageName]
一種是: [UIImage imageWithContentsOfFile:path]
后面我再解釋為什么需要對比這兩種加載方式,先上加載的結果吧型豁。
1>使用[UIImage imageWithContentsOfFile:path]
2>使用[UIImage imageNamed:imageName]
兩種方式都實際滑動一分鐘, 可以清晰的看到,兩種加載方式一開始都幀數(shù)很低,但是使用imageNamed: 的很快幀數(shù)就恢復到60幀,但是使用imageWithContentsOfFile:會一直卡頓,那是因為使用imageNamed: 會緩存圖片,但是imageWithContentsOfFile: 則不會,而且 使用imageWithContentsOfFile: 出現(xiàn)了明顯的卡頓,出現(xiàn)了明顯的丟幀從曲線上來看能明顯看到兩種方式的差異問題僵蛛。
再來解釋我們使用的兩種加載方式,使用 imageWithContentsOfFile: 實際上是模擬網(wǎng)絡下載圖片到本地后,再從本地加載展示圖片的過程,imageNamed:方式則是模擬從Assets.xcassets 里加載圖片的情況,可以明顯看到蘋果是對從Assets.xcassets 里加載圖片做過優(yōu)化的尚蝌。
2.如何對圖片解碼部分進行優(yōu)化
方案很簡單: 解碼的過程是可以直接放在子線程中的,解碼完成后可以在主線程中將圖片賦值給imageView.image并且緩存下來,下次再次查找到相同的圖片直接在緩存中讀取就可以了墩瞳。
這個過程是不是聽起來很熟悉,是的,這個過程已經(jīng)有很有多的第三方庫實現(xiàn)過了,其中最有名的就是SDWebImage了,SDWebImage的解碼方法是decodedImageWithImage,使用了CGContextDrawImage,有興趣的小伙伴們可以抽空去看看驼壶,在這我就不贅述了,直接上優(yōu)化代碼:
[self queryImageCache:imageName block:^(UIImage *image) {
cell.contentImageView.image = image;
}];
- (void)queryImageCache:(NSString *)filename block:(void(^)(UIImage *image))block
{
//從內(nèi)存去取喉酌,如果沒取到热凹,就直接讀取文件,在緩存起來
UIImage *image = [self.memCache objectForKey:filename];
if(image)
{
dispatch_async(dispatch_get_main_queue(), ^{
if(block)
block(image);
});
}
else
{
//把解壓操作放到子線程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:@"jpg"];
UIImage *image = [UIImage imageWithContentsOfFile:path];
image = [UIImage decodedImageWithImage:image];
[self.memCache setObject:image forKey:filename];
// 同步主線程
dispatch_async(dispatch_get_main_queue(), ^{
if(block)
block(image);
});
});
}
}
實驗以上方法后再次進行之前的方法查看FPS和CPU使用情況
名稱 | FPS (平均) | CPU(平均) | 實驗時間 |
---|---|---|---|
imageWithContentsOfFile: | 47.8 | 28% | 1min |
imageNamed: | 58.8 | 10% | 1min3 |
優(yōu)化后 | 59.9 | 7% | 1min9 |
可以明顯看到不論是幀數(shù)還是CPU使用情況,優(yōu)化后的列表情況都明顯優(yōu)異多了,雖然這個過程SDWebImage已經(jīng)實現(xiàn)了,但是放在我還是想放在這里來講解下,希望對各位有所幫助泪电。
參考過以下大大的技術博客:
https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/
http://www.reibang.com/p/f9ef5dba9ba3?_dt_push=1
http://www.cocoachina.com/cms/wap.php?action=article&id=24599