iOS 网络图片优化

概述:

iOS 开发中,很多app的网络图片处理使用SDwebImage或YYKit的YYImage,使用方便、稳定、内部做了很多的优化处理。但也存在一些问题

1、SDWebImage库提供了一整套的网络图片异步下载和缓存机制,还增加了UIImageView、UIButton等的Category,方便显示网络图片。

2、存在的问题
由于网络图片一般不会有@2x和@3x之分,通过SDWebImage库下载的图片不加以处理就直接显示,会有一些常见的问题,如像素不对齐。
App中经常使用圆角图片,一般采用裁剪图片的方式;但是这些图片源来自服务器(本地圆角图片让UI直接提供就可以了),我们需要在SDWebImage基础上增加对网络圆角图片的处理。

GPU图层显示的优化

参考

一、像素不对齐
检测方式:

以下两种方式均可发现存在Misaligned Images问题的地方:

  • 模拟器调试时,打开模拟器的Debug - Color Misaligned Images菜单选项。最快捷,但仅限模拟器上查看。
  • Instrument性能检测时,选中Core Animation模板,在Display Settings中勾选Color Misaligned Images选项。可针对模拟器和真机,可查看真机上所有应用的像素混合情况。
问题定义:

打开开关后,看到部分视图会有黄色或洋红色(Magenta)的图层标记,代表其像素不对齐。

  • 不对齐:视图或图片的点数(point),不能换算成整数的像素值(pixel),导致显示视图的时候需要对没对齐的边缘进行额外混合计算,影响性能。
  • 洋红色:UIView的frame像素不对齐,即不能换算成整数像素值。
  • 黄色:UIImageView的图片像素大小与其frame.size不对齐,图片发生了缩放造成。
优化方式:

frame像素不对齐

借助ceilf()、floorf()、CGRectIntegral()等将小数点后数据除去即可。
使用floorf时,需要注意是否会因为向下取整而影响视图的显示。
像素不对称齐的元素一般为UILabel或UIImageView。

图片像素不对齐

图片是从服务端获取到的,大小不规则。直接在UIImageView上显示容易出现像素不对齐。

解决方法

将下载到的图片,缩放到与UIImageView对应的尺寸,再显示出来。

#import "UIImage+Additions.h"

/**
 图片缩放 避免图片像素不对齐
 需要缩放图片到与UIImageView对应的尺寸,且缩放后的图片的scale和[UIScreen mainScreen].scale相等,再显示出来。
 图片的缩放是相对耗时的,放在非主线程,更新图片在主线程
 @param size 要达到的尺寸 一般为UIImageView的size
 @return 缩放后的图片
 */
- (UIImage *)scaleImageWithSize:(CGSize)size{
    
    if (CGSizeEqualToSize(size, self.size)) {
        return self;
    }
    //创建上下文
    UIGraphicsBeginImageContextWithOptions(size, YES, [UIScreen mainScreen].scale);
    //绘图
    [self drawInRect:CGRectMake(0, 0, size.width, size.height)];
    //获取新图片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

2、像素混合
像素混合是指在某视图为透明背景色,GPU在渲染视图时,需要将该视图和下层视图混合(Blend)后才能计算出该像素点的实际颜色;这增加了GPU的工作,损耗了性能。
当图片是透明图片时,像素混合必然会发生。所以显示的图片最好是不透明的。
iPhone模拟器中的Debug ->Color Blended Layers选项 和 Core Animation ->Display Settings ->Color Blended Layers都可以将像素混合的部分显示出来。
发生了像素混合的区域显示红色,正常则显示绿色。

图片处理使用Core Graphics实现。在使用UIGraphicsBeginImageContextWithOptions创建上下文时候,opaque默认为YES,表示不透明;

3、圆角图片的问题
不建议的方案1:通过设置cornerRadius值和masksToBounds=YES实现圆角效果。因为它会触发GPU的离屏渲染,引起性能问题。模拟器中的Color Offscreen-Rendered可以检测是否发生离屏渲染(如果出现黄色就发生了离屏渲染)。
不建议的方案2:通过设置view.layer的mask属性,将另一个layer盖在view上,也可以实现圆角的效果,但是同样会触发离屏渲染,引起性能问题。
建议的方案:使用Core Graphics重新绘制带圆角的图片,虽然在显示上提升了性能,但是增加了绘制的工作,所以要做好异步绘制和缓存工作,尽可能避免重复绘制。使用SD的UIImage+Transform分类方法:

#import<UIImage+Transform.h>
- (nullable UIImage *)sd_roundedCornerImageWithRadius:(CGFloat)cornerRadius
                                              corners:(SDRectCorner)corners
                                          borderWidth:(CGFloat)borderWidth
                                          borderColor:(nullable UIColor *)borderColor;

二、内存暴增

使用SDWebImage和YYImage下载高分辨率图时,可能导致内存暴增

WeChatf1ddd36fc515c2a32895eacb8bf38b22.png

用instrument检测发现是SDWebImage和YYImage对图像进行解压缩操作时引起的,那么这两个框架使用CGBitmapContextCreate的目的是为了优化图片加载或者从本地加载图片后的解码过程,但decodeImageWithImage这个方法用于对图片进行解压缩并且缓存起来,以保证tableviews/collectionviews 交互更加流畅,但是如果是加载高分辨率图片的话,会适得其反,有可能造成上G的内存消耗。对于高分辨率的图片,应该禁止解压缩操作,相关的代码处理为:

解决方式一(推荐):设置SDWebImageOptions
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
image.png
解决方式二:SDWebImage5.0+已经没有此方法
#import <SDWebImage/SDImageCache.h>
#import <SDWebImage/SDWebImageDownloader.h>
// 禁用解压缩
- (void)loadView{
    [[SDImageCache sharedImageCache].config setShouldDecompressImages:NO];
    [[SDWebImageDownloader sharedDownloader] setShouldDecompressImages:NO];
}
//恢复原设置
- (void)dealloc{
    [[SDImageCache sharedImageCache].config setShouldDecompressImages:YES];
    [[SDWebImageDownloader sharedDownloader] setShouldDecompressImages:YES];
}

此方式既能保证高分辨率图不crash,也能保证其他地方,普通图片依旧可以通过解压缩进行优化。

另外在收到内存警告时,做如下操作:

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application{
    // 清空缓存(内存)
    [[SDImageCache sharedImageCache] clearMemory];
    // 清空磁盘图片
    [[SDImageCache sharedImageCache] clearDiskOnCompletion:nil];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // 清空缓存(内存)
    [[SDImageCache sharedImageCache] clearMemory];
    // 清空磁盘图片
    [[SDImageCache sharedImageCache] clearDiskOnCompletion:nil];
    // Dispose of any resources that can be recreated.
}

解决方式三:

参考

使用SDWebimage肯定都会做三件事,一判断本地是否有这张图,二有的时候直接从本地取图片,三没有的时候去网络下载。在内部都会使用到下面这个方法

image.png

image.png

发现这里面对图片的处理是直接按照原大小进行的,如果几千是分辨率这里导致占用了大量内存,所以我们需要在这里对图片做一次等比的压缩。在UIImage+MultiFormat这个类里面添加如下压缩方法,


+(UIImage *)compressImageWith:(UIImage *)image
{
    float imageWidth = image.size.width;
    float imageHeight = image.size.height;
    float width = 640;
    float height = image.size.height/(image.size.width/width);
    
    float widthScale = imageWidth /width;
    float heightScale = imageHeight /height;
    
    // 创建一个bitmap的context
    // 并把它设置成为当前正在使用的context
    UIGraphicsBeginImageContext(CGSizeMake(width, height));
    
    if (widthScale > heightScale) {
        [image drawInRect:CGRectMake(0, 0, imageWidth /heightScale , height)];
    }
    else {
        [image drawInRect:CGRectMake(0, 0, width , imageHeight /widthScale)];
    }
    
    // 从当前context中创建一个改变大小后的图片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    // 使当前的context出堆栈
    UIGraphicsEndImageContext();
    
    return newImage;
    
}

再在上面 UIImage *image = [[UIImage alloc] initWithData:data];代码后面对图片进行压缩

UIImage *image = [[UIImage alloc] initWithData:data];
if (data.length/1024 > 128) {
       image = [self compressImageWith:image];
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348