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
- (nullable UIImage *)sd_roundedCornerImageWithRadius:(CGFloat)cornerRadius
                                              corners:(SDRectCorner)corners
                                          borderWidth:(CGFloat)borderWidth
                                          borderColor:(nullable UIColor *)borderColor;

二、内存暴增

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

iOS 网络图片优化_第1张图片
WeChatf1ddd36fc515c2a32895eacb8bf38b22.png

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

解决方式一(推荐):设置SDWebImageOptions
- (void)sd_setImageWithURL:(nullable NSURL *)url
          placeholderImage:(nullable UIImage *)placeholder
                   options:(SDWebImageOptions)options
iOS 网络图片优化_第2张图片
image.png
解决方式二:SDWebImage5.0+已经没有此方法
#import 
#import 
// 禁用解压缩
- (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肯定都会做三件事,一判断本地是否有这张图,二有的时候直接从本地取图片,三没有的时候去网络下载。在内部都会使用到下面这个方法

iOS 网络图片优化_第3张图片
image.png

iOS 网络图片优化_第4张图片
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];
}

你可能感兴趣的:(iOS 网络图片优化)