http://www.cocoachina.com/ios/20150128/11053.html

1.引言

过去的几年里,iOS应用在视觉方面越来越吸引人。图像展示是其中很关键的部分,因为大部分图像展示都需要下载并且渲染。大部分开发者都要使用图像填充表格视图(table views) 或者 集合视图(collection views) 。下载图片消耗一些资源(如蜂窝数据、电池以及CPU 等)。为了减少资源消耗,一些缓存模型也应运而生。

为了获得良好的用户体验,当我们缓存和加载图像时,了解iOS底层如何处理是很重要的。此外,大多数使用了图片缓存的开源库也是个不错解决方案。

2.常用的解决途径

  • 异步下载图像

  • 处理图像(拉伸,去红眼,去边框)以便展示

  • 写入磁盘

  • 需要时从磁盘读取并展示

// 假设我们有一个 NSURL *imageUrl and UIImageView *imageView, 我们需要通过NSURL下载图片并在UIImageview上展示
if ([self hasImageDataForURL:imageUrl] {
    NSData *data = [self imageDataForUrl:imageUrl];
    UIImage *image = [UIImage imageWithData:imageData];
    dispatch_async(dispatch_get_main_queue(), ^{
        imageView.image = image;
    });
} else {
    [self downloadImageFromURL:imageUrl withCompletion:^(NSData *imageData, …) {
        [self storeImageData:imageData …];
        UIImage *image = [UIImage imageWithData:imageData];
        dispatch_async(dispatch_get_main_queue(), ^{
            imageView.image = image;
        });
    }];
}

FPS 简介

  • UI渲染理想情况FPS=60

  • 60FPS => 16.7ms 每帧 这就意味着 如果任何主线程操作大于16.7ms,动态FPS将会下降,因为cpu忙于处理其他事情 而不是渲染UI。

3.常用解决途径的缺点

  • 从磁盘加载图像或文件时间消耗昂贵(磁盘读取比内存读取慢大概10 - 1000倍,如果是SSD硬盘 可能与内存读取速度更接近(大概慢10倍)。参考这里的比较:Introduction to RAM Disks。如果使用SSD,将获得接近内存的速度(大概比内存访问速度慢十倍),但目前还没有手机和平板集成SSD模块。

  • 创建UIImage实例将会在内存区生成一个图片的压缩版。但是压缩后的图像太小且无法渲染,如果我们从磁盘加载图像,图像甚至都没有加载到内存。解压图片同样也很消耗资源。

  • 设置imageView的image属性,这种情况下将会创建一个CATransaction并加入主循环中。在下一次循环迭代中,CATransaction会对任何设置为layer contents的图像进行拷贝。

拷贝图像包含以下过程:

  • 给文件io 和 解压缩 分配缓冲区

  • 读取磁盘数据到内存

  • 解压图像数据(生成原位图) - 高cpu消耗

  • CoreAnimation 使用解压数据并渲染

字节位没有正确对齐的图像将被CoreAnimation拷贝,以修复字节位对齐并使之能被渲染。这一点在Apple 文档里没有说明,但是使用Instruments表明 CA::Render::copy_image会执行此操作,即使Core Aniation 即使没有拷贝图像。

从iOS7 开始,第三方应用不能使用JPEG硬件解码器。这意味着我们只能使用慢很多的软解码器。这一点在FastImageCache团队的 GitHub主页以及 Nick Lockwood的推文上都有指出。

4.一个强大的iOS图像缓存需包含以下部分: 

  • 异步下载图像,尽可能减少使用主线程队列。

  • 使用后台队列解压图像。这是个复杂的过程,请阅读Avoiding Image Decompression Sickness(http://www.cocoanetics.com/2011/10/avoiding-image-decompression-sickness/)。

  • 在内存和磁盘上缓存图像。在磁盘上缓存图像很重要,因为app可能因为内存不足而被强行关闭或者需要清理内存。这种情况下,重新从磁盘加载图像比下载会快很多。备注:如果使用NSCache作为内存缓存,当有内存警告时,NSCache会清空缓存内容。NSCache相关细节请查看nshipster 上这篇文章:NSCache

  • 保存解压过的图片到硬盘以及内存中,以避免再次解压。

  • 使用GCD 和 blocks,这将使得代码更加高效和简单。如今GCD 和 blocks是异步操作时必需的。

  • 最好使用UIImageView的分类以便集成

  • 最好在下载后以及存入到缓存前能够处理图像

iOS图像优化 

更多的成像相关以及SDK框架(CoreGraphics,ImageIO,CoreAnimation,CoreImage)工作原理,CPU vs GPU 等,请阅读@rsebbe的文章:Advanced Imaging on iOS

Core Data 是一个好的选择吗?

这有一篇文章--CoreData 对比File System,实现图像缓存的基准测试。结果File System的表现更好(正如我们所预期的)

看一看上面罗列的观点,自己实现图像缓存不仅复杂,耗时而且痛苦。这也是为什么我倾向于使用开源的图像缓存解决方案。你们大部分已经听说过SDWebImage或new FastImageCache。

为了让你知道哪个开源库最适合你,我做了测试并且分析它们如何满足上述要求。

5.基准测试

测试库:

注:AFNetworking 加入对比是由于其自iOS7后在磁盘缓存方面出色的表现(基于NSURLCache实现)

测试场景

对于每个库,我都会使用全新的测试app,然后启动app,等所有图像加载完后,慢慢滑动。然后以不同力度来回滑动(从慢到快)。接着关掉app强制应用从磁盘缓存中加载图像,最后重复以上测试场景。

关于测试app工程

-相关demo可以在Github找到并获取,名字是ImageCachingBenchmark。同时还有本次实验的图表、结果数据表以及更多。

-请注意,请注意Github上的工程和图像缓存库都需要做一些调整,以便能让我们看到每一张缓存的图片都能够被加载出来。由于我不想检查Cocoapods源码文件(不是个好习惯),所以需要对Cocoapods clean后重新编译工程代码 。目前Github上的版本与我做测试的版本有些差别。

-如果你们想重新跑一下测试,你需要编写相同completionBlock用于图像加载,所有库得要跟默认的SDWebImage一样返回SDImageCacheType。

最快与最慢的设备对比结果

在Github工程上能看到完整的基准测试结果,由于这些表格很大,我只使用运行最快的设备iPhone 5s和运行最慢的iPhone 4来测试。

汇总:

表格名词解释

- 异步下载 = 库支持异步下载

-后台解压 =通过后台队列或线程执行图像解压

-存储解压 = 存储解压后的图像版本

-内存/磁盘缓存 = 支持内存/磁盘缓存

-UIImageView分类 = 库中含UIImageView 类别

-从内存/磁盘 = 从缓存(内存/磁盘)中读取的平均时间

6.结论

-从头开始编写iOS图像缓存组件很困难

-SDWebImage 和 AFNetworking 是稳定的工程。由于有很多贡献者,这样保证代码能够及时得到维护。FastImageCache在维护方面更新很快。

-基于以上所有数据,我认为SDWebImage 在目前是一个很好的解决方案。即使有些工程使用AFNetworking 或 FastImageCache更好。但是这些都依赖于项目需求。

iOS image caching. Libraries benchmark (SDWebImage vs FastImageCache)的更多相关文章

  1. iOS图片加载速度极限优化—FastImageCache解析

    FastImageCache是Path团队开发的一个开源库,用于提升图片的加载和渲染速度,让基于图片的列表滑动 优化点 iOS从磁盘加载一张图片,使用UIImageVIew显示在屏幕上,需要经过以下步 ...

  2. iOS 图片加载速度极限优化—FastImageCache解析

    FastImageCache是Path团队开发的一个开源库,用于提升图片的加载和渲染速度,让基于图片的列表滑动起来更顺畅,来看看它是怎么做的.优化点iOS从磁盘加载一张图片,使用UIImageVIew ...

  3. Is Safari on iOS 6 caching $.ajax results? post Cache

    https://stackoverflow.com/questions/12506897/is-safari-on-ios-6-caching-ajax-results Since the upgra ...

  4. iOS图片加载-SDWebImage

    一.SDWebImage内部实现过程 1, 入口 setImageWithURL:placeholderImage:options: 会先把 placeholderImage 显示,然后  SDWeb ...

  5. OC高仿iOS网易云音乐AFNetworking+SDWebImage+MJRefresh+MVC+MVVM

    效果 因为OC版本大部分截图和Swift版本一样,所以就不再另外截图了. 列文章目录 因为目录比较多,每次更新这里比较麻烦,所以推荐点击到主页,然后查看iOS云音乐专栏. 目简介 这是一个使用OC语言 ...

  6. SDWebImage 官方文档

    API documentation is available at CocoaDocs - SDWebImage Using UIImageView+WebCache category with UI ...

  7. ios项目里扒出来的json文件

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 13.0px Menlo; color: #000000 } p.p2 { margin: 0.0px 0. ...

  8. Github上关于iOS的各种开源项目集合(强烈建议大家收藏,查看,总有一款你需要)

    下拉刷新 EGOTableViewPullRefresh - 最早的下拉刷新控件. SVPullToRefresh - 下拉刷新控件. MJRefresh - 仅需一行代码就可以为UITableVie ...

  9. iOS - 开发类库

    开发类库   UI 项目名称 项目信息 1.MJRefresh 仅需一行代码就可以为UITableView或者CollectionView加上下拉刷新或者上拉刷新功能.可以自定义上下拉刷新的文字说明. ...

随机推荐

  1. SAP HANA创建层次结构的属性视图(Hierarchy Attribute View)

    在产品表中,数据是具有一定层次结构的 1. 在Content相应的package下面右键点击Attribute View,新建Attribute View 填写相关信息,注意:我们所建的依然是标准视图 ...

  2. Tiny6410之按键裸机驱动

    操作步骤: 第一步:查看开发板电路原理图 找到LED 和按键的管脚所对应的寄存器 LED:(见Tiny6410之LED裸机驱动) nLED_1 - GPK4 nLED_2 - GPK5 nLED_3 ...

  3. 用TextKit实现图文混排(转载)

    Textkit是iOS7新推出的类库,其实是在之前推出的CoreText上的封装,有了这个TextKit,以后不用再拿着CoreText来做累活 了,根据苹果的说法,他们开发了两年多才完成,而且他们在 ...

  4. springmvc+maven

    http://blog.csdn.net/zht666/article/details/8673609/

  5. 华硕笔记本进pe之前的设置

    1.开机的时候长按F2键进入BIOS界面,通过方向键进[Secure]菜单,通过方向键选择[Secure Boot Control]选项,将其设定为 "Disabled"2.通过方 ...

  6. Java Script 学习笔记

    JS编程习惯类: 1. 命名 著名的变量命名规则 只是因为变量名的语法正确,并不意味着就该使用它们.变量还应遵守以下某条著名的命名规则: Camel 标记法 首字母是小写的,接下来的字母都以大写字符开 ...

  7. ios 开发证书 appids 描述文件关系

    当你准备进行真机测试或者发布应用到App Store上去的时候, 免不了要申请相应的证书.(Development--测试证书. Distribution--发布证书) 进入证书管理相应网站https ...

  8. Path.OS 模块的使用方法(转自DK的博客)

    Python os.path模块 使用方法 os.path.abspath(path) #返回绝对路径 os.path.basename(path) #返回文件名 os.path.commonpref ...

  9. java 守护线程

    守护线程生命周期: 守护线程是运行在后台的一种特殊线程, 它独立于控制终端并且周期性地执行某种任务或者等待处理某些发生的事件. 也就是说守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”. 当J ...

  10. iis虚拟目录或应用程序不继承父站点的web.config配置信息

    A为主站点 B为A的应用程序站点 再A的web.config中对不想继承的节点用location 套起来.如下: <location path="." allowOverri ...