iOS中的图片使用方式、内存对比和最佳实践

iOS中的图片使用方式、内存对比和最佳实践

预备知识

我们的对比主要关注内存的占用情况。对比的格式是jpg和png这两种最广泛使用的格式,分别代表了有损压缩和无损压缩;关于它们的特点和介绍,可以参考郭耀源的这篇文章:移动端图片格式调研。我们可以看到,在iOS设备上它们的解码消耗在一个量级,速度较快。
wwdc2018苹果重点关注了图片的内存占用情况(因为此前大家的通用做法实际上是相对低效的),并且给了大家一些指导。这里我们会用demo实验的方式(并且通过工具来进行benchmark),为大家揭示一些事实和现象,供大家去理解和分析,从而去形成我们代码的最佳实践。

Demo:DownSampleDemo

场景

首先,苹果告诉我们,图片在应用中主要的内存占用(这通常发生在图片要被载入并显示时)和图片本身的大小实际是无关的;重要的是图片的尺寸。decode buffer的计算方式是width*height*N,这里的N通常是4(最常见的ARGB888格式),N取决于你显示所使用的格式。但很多时候,我们会直接把一个UIImage传递给UIImageView, 实际上该View的尺寸可能远远的小于UIImage本身。

使用图片的三种方式:

方式A
image1.image = UIImage(named: "000.jpg")
复制代码

这是我们最通常使用图片的方式,可能有人会想到imagewithcontentsoffile,实际上它和上面的方法只有一个不同之处: 正常情况下imageNamed会缓存这个图片在整个app存续期间,这样就无需重复的decode;而imagewithcontentsoffile则没有这个缓存,图片不使用了就会释放内存。但这对我们的case并无帮助,因为我们是在使用这些图片,并且观察它们的内存占用.

方式B
+ (UIImage*)OriginImage:(UIImage *)image scaleToSize:(CGSize)size
{
    // 创建一个bitmap的context
    // 并把它设置成为当前正在使用的context
    UIGraphicsBeginImageContext(size);
    
    // 绘制改变大小的图片
    [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
    
    // 从当前context中创建一个改变大小后的图片
    UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
    
    // 使当前的context出堆栈
    UIGraphicsEndImageContext();
    
    // 返回新的改变大小后的图片
    return scaledImage;
}
复制代码

这是一种被广泛使用的缩放图片的办法。

方式C
    func downsample(imageAt imageURL: URL, to pointSize: CGSize, scale: CGFloat) -> UIImage
    {
        let sourceOpt = [kCGImageSourceShouldCache : false] as CFDictionary
        // 其他场景可以用createwithdata (data并未decode,所占内存没那么大),
        let source = CGImageSourceCreateWithURL(imageURL as CFURL, sourceOpt)!
        
        let maxDimension = max(pointSize.width, pointSize.height) * scale
        let downsampleOpt = [kCGImageSourceCreateThumbnailFromImageAlways : true,
                             kCGImageSourceShouldCacheImmediately : true ,
                             kCGImageSourceCreateThumbnailWithTransform : true,
                             kCGImageSourceThumbnailMaxPixelSize : maxDimension] as CFDictionary
        let downsampleImage = CGImageSourceCreateThumbnailAtIndex(source, 0, downsampleOpt)!
        return UIImage(cgImage: downsampleImage)
    }
复制代码

这是苹果介绍给我们的新方法。

关于测试

测试设备: 1.iphone8 11.4 2.iphone6 12beta2
测试图片格式: png/jpg
测试的图片使用方式:3种
我们将2560*1440大小的图片放到282*138的view中,对于缩放我们使用2x的格式来显示以保证其效果。

实验数据的细节在DownSampleDemo的ViewController.swift的注释中;直接告诉大家结论:

  1. xcode面板上所显示的内存占用并不可靠,在很多case下应用占用的内存要比其上显示的多的多(特别是对于iOS11.4的设备)。但可以用来粗略的查看内存占用,如果这里超了很多,那肯定是超了
  2. 可以使用instruments的allocation和memory debugging的memgraph+命令行分析这两种方式来观测内存的占用
  3. 第一种方式下jpg载入后的内存分为imageIO和IOkit两部分,png载入只有imageIO部分,jpg内存占用比png要少不少
  4. 第二种方式的数据在xcode面板上失真,通过另外两种方式可以看到它非常不靠谱。内存消耗甚至可能超过第一种。
  5. 第三种方式的内存占用非常完美,严格的遵守了公式计算出来的大小(跟你downsample后的尺寸有关),内存占用在CG raster data中。
  6. memgraph命令: vmmap --summary xxx.memgraph
  7. allocation我们只需要看VM allocation栏目下的dirty size和swapped size,这里需要手动的打开snapshot才能看到该栏目的数据,并且需要适当的重复多次运行才能保证结果的正确性

VMTracker运行截图:

memgraph命令结果截图:

最后,来自苹果的最佳实践:强烈推荐大家使用downsample来处理大图!Thank you!:]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值