前段時(shí)間看了篇IOS性能優(yōu)化的文章:https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/
里面提到了--圖片的解碼
當(dāng)你用 UIImage 或 CGImageSource 的那幾個(gè)方法創(chuàng)建圖片時(shí)欢嘿,圖片數(shù)據(jù)并不會(huì)立刻解碼。圖片設(shè)置到 UIImageView 或者 CALayer.contents 中去,并且 CALayer 被提交到 GPU 前,CGImage 中的數(shù)據(jù)才會(huì)得到解碼湾趾。這一步是發(fā)生在主線程的悴品,并且不可避免。如果想要繞開(kāi)這個(gè)機(jī)制截驮,常見(jiàn)的做法是在后臺(tái)線程先把圖片繪制到 CGBitmapContext 中婆瓜,然后從 Bitmap 直接創(chuàng)建圖片快集。目前常見(jiàn)的網(wǎng)絡(luò)圖片庫(kù)都自帶這個(gè)功能。
這就是說(shuō)圖片解碼需要消耗比較多的時(shí)間廉白,在即將顯示前个初,圖片才會(huì)得到解碼顯示。
一蒙秒、圖片解碼產(chǎn)生的性能問(wèn)題
下面我試試圖片解碼到底有多卡勃黍,我拿了10張超過(guò)1MB的圖片進(jìn)行測(cè)試宵统。
代碼很簡(jiǎn)單晕讲,就是一個(gè)tableView,在cell里面顯示圖片马澈。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
if(!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
UIImageView *headImageView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 6, 80, 100-12)];
headImageView.tag = 10;
[cell.contentView addSubview:headImageView];
}
NSString *fileName = [NSString stringWithFormat:@"Start%zi",indexPath.row%12];
@autoreleasepool{
UIImage *image = [UIImage imageNamed:fileName];
UIImageView *imageView = [cell.contentView viewWithTag:10];
imageView.image = image;
}
return cell;
}
1瓢省、通過(guò)imageNamed加載圖
@autoreleasepool{
UIImage *image = [UIImage imageNamed:fileName];
UIImageView *imageView = [cell.contentView viewWithTag:10];
imageView.image = image;
}
2、通過(guò)imageWithContentsOfFile加載圖片
@autoreleasepool{
// UIImage *image = [UIImage imageNamed:fileName];
NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:@"png"];
UIImage *image = [UIImage imageWithContentsOfFile:path];
UIImageView *imageView = [cell.contentView viewWithTag:10];
imageView.image = image;
}
兩種加載方式痊班,一開(kāi)始都是比較卡勤婚,好像imageNamed方式有緩存,后面再滑動(dòng)tableView不卡了涤伐,因?yàn)榫彺媪私獯a后的圖片馒胆。但imageWithContentsOfFile這種方式缨称,一直都是卡頓,證明沒(méi)有緩存祝迂,每次顯示都進(jìn)行了一次解碼睦尽。
給imageView.image賦值時(shí)候,才會(huì)發(fā)生圖片解碼型雳,如果不執(zhí)行這代碼当凡,發(fā)現(xiàn)沒(méi)產(chǎn)生什么卡頓,證明imageNamed等的圖片加載方法耗時(shí)不多纠俭,主要的耗時(shí)操作是在解碼階段沿量。
二、圖片解碼的性能優(yōu)化
對(duì)圖片解碼的性能優(yōu)化冤荆,可以考慮把圖片解碼操作放到子線程執(zhí)行朴则。然后再把解碼后的圖片賦值給imageView.image,如果發(fā)現(xiàn)圖片已經(jīng)是解碼過(guò)的钓简,就不會(huì)再發(fā)生解碼操作佛掖,就可以達(dá)到優(yōu)化效果。
SDWebImage的實(shí)現(xiàn)思路就是這樣涌庭,從網(wǎng)絡(luò)獲取圖片后芥被,進(jìn)行了一次解碼操作,再緩存到內(nèi)存和磁盤坐榆,以供顯示拴魄。
SDWebImageDecoder的解碼方法decodedImageWithImage,里面代碼也不難席镀,有興趣可以詳細(xì)看看匹中,當(dāng)然,網(wǎng)上也有一大堆源碼分析豪诲。
下面顶捷,我就要用這方法優(yōu)化上面的代碼:
- (void)queryImageCache:(NSString *)filename block:(void(^)(UIImage *image))block
{
//從內(nèi)存去取,如果沒(méi)取到屎篱,就直接讀取文件服赎,在緩存起來(lái)
UIImage *image = [self.memCache objectForKey:filename];
if(image)
{
dispatch_async(dispatch_get_main_queue(), ^{
if(block)
block(image);
});
}
else
{
dispatch_async(_ioQueue, ^{
@autoreleasepool{
NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:@"png"];
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);
});
}
});
}
}
我把解碼操作放到了子線程,解碼完成后就緩存到內(nèi)存交播,以供下次直接使用重虑,執(zhí)行如下:
[self queryImageCache:fileName block:^(UIImage *image) {
UIImageView *imageView = [cell.contentView viewWithTag:10];
imageView.image = image;
}];
return cell;
不會(huì)出現(xiàn)卡頓現(xiàn)象,而且占用的內(nèi)存比直接使用imageNamed少秦士。