深入分析iOS应用中对于图片缓存的管理和使用

我们的 iOS 应用都包含了大量的图像。创建富有吸引力的视图,主要依赖于大量的装饰图片,所有这些首先必须从远程服务器获取。如果每次打开应用都要从服务器一次又一次的获取每个图像,那么用户体验肯定达不到好的效果,所以本地缓存远程图像是非常有必要的。

两种方式加载本地图片
1.通过imageNamed:方法加载图片
用过这种方式加载图片,一旦图片加载到内存中,那么就不会销毁,一直到程序退出。(也就是说imageNamed:会有图片缓存的功能,当下次访问图片的时候速度会更快。)
用这种方式加载图片,图片的内存管理并不受程序员控制。

代码如下:

UIImage *image = [UIImage imageNamed: @“image”]

的意思是创建一个UIImage对象,并不是说image这个本身就是一张图片,而是image指向一张图片。在创建这个对象的时候实际上并没有把真正的图片加载到内存里,而是等到用到图片的时候才会加载。
如上例,如果把image对象设置为nil,如果是其它对象,那么没有强指针指向一个对象,这个对象就会销毁;但是即使image = nil,它会指向的图片资源也不会销毁。
2.通过imageWithContentsOfFile:方式加载图片
使用这个方法加载图片,当指向图片对象的指针销毁或指向其它对象,这个图片对象没有其它强指针指向,这个图片对象会销毁,不会一直在内存中停留。

因为没有缓存,所以如果相同的图片多次加载,那么也会有多个图片对象来占用内存,而不是用缓存的图片。

使用这个方法,需要file的全路径(之前用NSString, NSArray之类的加载文件也是一样的,比如stringWithContentsOfFile:,看到file就知道是需要传入全路径。)

代码如下:

NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"png"];
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];

注意如果图片在Images.xcassets中,是不能使用这个方法的。所以说想要自己进行图片的内存管理(不希望有缓存图片),那么要将图片资源直接拖入工程,而不是放在Images.xcassets中。

快速队列和慢速队列
我们设置了两个队列,一个串行,一个并行。在屏幕上被迫切要求的图片进入并行队列(fastQueue),可能晚点才需要的图片进入串行队列(slowQueue)。
就UITableView的实现而言,这意味着在屏幕上的表格单元从fastQueue获取图片, 每个关闭的屏幕行的图片从slowQueue预加载。

现在不需要处理图片

假设我们要从服务器上请求包含30条事件的一页资讯回来,一旦这些内容请求回来时我们就可以排队等待预取其中的每一张图。

代码如下:

- (void)pageLoaded:(NSArray *)newEvents {   
    for (SGEvent *event in newEvents) {       
       [SGImageCache slowGetImageForURL:event.imageURL thenDo:nil];   
    }
}

slowGetImageForURL:这个方法将图片添加到slowQueue这个队列当中,允许它们在不阻塞网络通信的前提下被一张一张的取出来。
thenDo:这个代码块在这里是没有被实现,是因为我们目前还不需要对图片做任何事情。所有我们需要做的就是确保它们在本地磁盘缓存当中,并且随时准备在屏幕上滑动表格时来使用。

现在就要处理图片

显示在屏幕上的表格希望立即显示它们的图片,所以在table cell子类当中实现:

代码如下:

- (void)setEvent:(SGEvent *)event {   
    __weak SGEventCell *me = self;   
    [SGImageCache getImageForURL:event.imageURL thenDo:^(UIImage *image) {       
       me.imageView.image = image;    }
    ];
}

getImageForURL:这个方法将抓取图片的过程添加到fastQueue这个队列当中,意味着只要iOS系统允许,它们会并行被地执行。如果抓取图片的过程已经存在于slowQueue队列当中,它会被移动到fastQueue队列中,从而避免重复请求。

一直异步

等等,getImageForURL:不是一个异步方法吗?如果你明知道图片已经在缓存中,但是却不想在主线程上立即使用它吗?直觉告诉你那是错误的。
从磁盘上加载图片太费资源,同样解压图片也会费很多资源。可以在滑动的过程当中进行配置和添加表格,这最后一件你想在滑动表格时做的事是很危险地,因为它会阻塞主线程,会有卡顿的现象出现。
使用getImageForURL:可以让磁盘加载的动作脱离主线程,于是当thenDo:这个用于收尾工作的代码块执行的时候它已经有了一个UIImage实例,从而不会有滑动卡顿的危险。如果图片已经存在于本地缓存当中,用于收尾工作的代码块会在下一次运行周期执行,并且用户不会注意到两者之间的差别。他们会注意到的是滑动不会卡顿了。

现在,不需要你快速执行

如果用户很快的滑动表格到底部,几十或几百个表格单元会出现在屏幕上,并向fastQueue请求图片数据,然后很快地从屏幕上消失。突然间这个并行地队列会将大量实际上不再需要的图片请求充斥进网络。当用户最终停止滑动时,那些当前屏幕上相应的表格单元视图会将它们的图片请求至于那些并不急需的请求后面,因此网络阻塞了。
这就是 wheremoveTaskToSlowQueueForURL:这个方法的产生的原因.

代码如下:

// a table cell is going off screen-
(void)tableView:(UITableView *)table       
didEndDisplayingCell:(UITableViewCell *)cell       
forRowAtIndexPath:(NSIndexPath*)indexPath {   
     // we don't need it right now, so move it to the slow queue            
     [SGImageCache moveTaskToSlowQueueForURL:[[(id)cell event] imageURL]];
}

这确保在fastQueue中的只会有真正需要被快速执行的任务。任何以前认为需要快速执行但现在不需要的任务会被移至slowQueue中。

重点和选择

已经有相当多的iOS图片缓存库。它们中一些库只针对某些应用场景,一些库提供了不同场景一定的可扩展性。我们的库即没有专门针对某些应用场景,也没有太多大而全的特性。针对我们的用户我们有三类基本的重点:
重点 1: 最好的帧率
很多的库都非常专注在这一点上,使用一些高度定制和复杂的方法,尽管基准没有决定性地显示这样有效。我们发现最好的帧率由这些决定:
将对磁盘的访问(并且几乎其它的所有)脱离主线程。
使用UIImage的内存缓存来避免不必要的磁盘访问和图片解压。

重点 2: 让最最重要的图片优先显示
大多数的库都考虑让队列管理成为别人关心的事。对于我们的应用,这几乎是最重要的点。
让正确的图片在正确的时间显示在屏幕上可以归结为一个简单的问题:“我们现在就需要它显示还是过一会儿?”。那些需要立即显示的图片是并行加载地,而其它所有东西都被添加到串行队列中。所有之前急迫的事但现在不急迫的话就会从fastQueue分到slowQueue中。并且当fastQueue在工作时,slowQueue是处于挂起状态的。
这让那些急需显示的图片可以单独访问网络,同时也确保了一张非急需显示的图片可以在过一会成为一张急需显示的图片,因为它已经存到了缓存当中,随时准备用于显示。

重点 3: 尽可能简单的API
大多数库都做到了这一点。许多库为了隐藏细节内容而提供了UIImageView的分类,并且许多库让抓取一张图片的流程变得尽可能的便利。针对我们经常做的三件事,我们的库选定了三个主要的方法:
快速抓到一张图

代码如下:

__weak SGEventCell *me = self;[SGImageCache getImageForURL:event.imageURL thenDo:^(UIImage *image) {    me.imageView.image = image;}];

排队等待一张我们一会才需要的图片

代码如下:

[SGImageCache slowGetImageForURL:event.imageURL thenDo:nil];

通知缓存一张急需显示的图已经不需要立刻显示

代码如下:

[SGImageCache moveTaskToSlowQueueForURL:event.imageURL];

结论

通过专注于预取,队列管理,从主线程移除耗时的任务,并且依赖于UIImage内置的内存缓存,我们努力从一个简单的软件包中得到好的结果。

(0)

相关推荐

  • iOS开发之清除缓存功能的实现

    前言 移动应用在处理网络资源时,一般都会做离线缓存处理,其中以图片缓存最为典型,其中很流行的离线缓存框架为SDWebImage.但是,离线缓存会占用手机存储空间,所以缓存清理功能基本成为资讯.购物.阅读类app的标配功能. 清除缓存基本上都是在设置界面的某一个Cell,于是我们可以把清除缓存封装在某一个自定义Cell中 如下图所示: 实现的具体步骤 使用注意:过程中需要用到第三方库,请提前安装好:SDWebImage.SVProgressHUD. 1. 创建自定义Cell,命名为GYLClear

  • iOS清除所有缓存的实例代码

    本文介绍了iOS清除所有缓存的实例代码,分享给大家,具体如下: 计算缓存 NSString *libPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0]; CGFloat fileSize=[self folderSizeAtPath:libPath]; - (float ) folderSizeAtPath:(NSString*) folderPath{ NSFileMa

  • iOS中设置清除缓存功能的实现方法

    绝大多数应用中都存在着清楚缓存的功能,形形色色,各有千秋,现为大家介绍一种最基础的清除缓存的方法.清除缓存基本上都是在设置界面的某一个Cell,于是我们可以把清除缓存封装在某一个自定义Cell中,如下图所示: 具体步骤 使用注意:过程中需要用到第三方库,请提前安装好:SDWebImage.SVProgressHUD. 1. 创建自定义Cell,命名为GYLClearCacheCell 重写initWithStyle:(UITableViewCellStyle)style reuseIdentif

  • iOS中的NSURLCache数据缓存类用法解析

    在IOS应用程序开发中,为了减少与服务端的交互次数,加快用户的响应速度,一般都会在IOS设备中加一个缓存的机制.使用缓存的目的是为了使用的应用程序能更快速的响应用户输入,是程序高效的运行.有时候我们需要将远程web服务器获取的数据缓存起来,减少对同一个url多次请求.下面将介绍如何在IOS设备中进行缓存. 内存缓存我们可以使用sdk中的NSURLCache类.NSURLRequest需要一个缓存参数来说明它请求的url何如缓存数据的,我们先看下它的CachePolicy类型.    1.NSUR

  • IOS 缓存文件的清除实现代码

    移动互联网 APP 的应用开发,必须要时刻注意用户体验,以免造成APP 或者手机及其他移动设备的卡死情况,以下是对缓存文件的处理. 移动应用在处理网络资源时,一般都会做离线缓存处理,其中以图片缓存最为典型,其中很流行的离线缓存框架为SDWebImage. 但是,离线缓存会占用手机存储空间,所以缓存清理功能基本成为资讯.购物.阅读类app的标配功能. 今天介绍的离线缓存功能的实现,主要分为缓存文件大小的获取.清除缓存文件的实现. 1. 获取缓存文件的大小 -( float )readCacheSi

  • iOS中的缓存计算和清除完整实例代码

    1.首先,一般我们项目中的缓存一般分为2大块,一个是自己缓存的一些数据;还有一个就是我们使用的SDWebImage这个第三方库给我们自动缓存的图片文件缓存了 <1>怎么计算缓存大小(主要是利用系统提供的NSFileManager类来实现) $1.单个文件大小的计算 -(long long)fileSizeAtPath:(NSString *)path{ NSFileManager *fileManager=[NSFileManager defaultManager]; if([fileMana

  • IOS获取缓存文件的大小并清除缓存文件的方法

    移动应用在处理网络资源时,一般都会做离线缓存处理,其中以图片缓存最为典型,其中很流行的离线缓存框架为SDWebImage. 但是,离线缓存会占用手机存储空间,所以缓存清理功能基本成为资讯.购物.阅读类app的标配功能. 今天介绍的离线缓存功能的实现,主要分为缓存文件大小的获取.清除缓存文件的实现. 1. 获取缓存文件的大小 -( float )readCacheSize { NSString *cachePath = [NSSearchPathForDirectoriesInDomains (N

  • Objective-C的缓存框架EGOCache在iOS App开发中的使用

    EGOCache简介 EGOCache is a simple, thread-safe key value cache store. It has native support for NSString, UI/NSImage, and NSData, but can store anything that implements <NSCoding>. All cached items expire after the timeout, which by default, is one da

  • iOS缓存文件大小显示功能和一键清理功能的实现方法

    缓存占用了系统的大量空间,如何实时动态的显示缓存的大小,使用户清晰的了解缓存的积累情况,有效的进行一键清理呢? 为方便读者和未来自己更好理解,我们创建这样场景.(在表视图的清除缓存一单元格内创建一个UILabel *cacheLabel用于显示当前缓存,当点击单元格弹出提示框,点击确定,清除缓存). 下面是实现代码: #pragma mark - 计算缓存大小 - (NSString *)getCacheSize { //定义变量存储总的缓存大小 long long sumSize = 0; /

  • 深入分析iOS应用中对于图片缓存的管理和使用

    我们的 iOS 应用都包含了大量的图像.创建富有吸引力的视图,主要依赖于大量的装饰图片,所有这些首先必须从远程服务器获取.如果每次打开应用都要从服务器一次又一次的获取每个图像,那么用户体验肯定达不到好的效果,所以本地缓存远程图像是非常有必要的. 两种方式加载本地图片 1.通过imageNamed:方法加载图片 用过这种方式加载图片,一旦图片加载到内存中,那么就不会销毁,一直到程序退出.(也就是说imageNamed:会有图片缓存的功能,当下次访问图片的时候速度会更快.) 用这种方式加载图片,图片

  • iOS WebView中使用webp格式图片的方法

    webp格式图片 webp格式图片是google推出的,相比jpg png有着巨大的优势,同样质量的图片webp格式的图片占用空间更小,在像电商这样图片比较多的App中,使用webp格式图片会很有优势. 引言 很早之前,我们的项目中就已经采用了webp格式,但是由于webView本身并不能解析webp格式,所以我们基于webView的文章详情页就无法使用到这项优化. 那么有没有什么办法能实现呢?当然是有的. 在开始技术讲解之前需要先说明,本文的技术方案,是基于本项目的情况:文章的正文大部分通过接

  • Flutter图片缓存管理ImageCache原理分析

    目录 引言 PaintingBinding 减少图片缓存 增大阀值 思考 引言 设计: 嗯? 这个图片点击跳转进详情再返回图片怎么变白闪一下呢?产品: 是啊是啊! 一定是个bug开发: 囧囧囧 在开发过程中, 也许你也遇到过这样一个场景. 进入一个页面后,前一个页面的图片都会闪白一下. 或者在列表中,加载很多列表项后,之前列表中的图片都需要重新加载.你有没有想过这一切的原因是什么呢? 没错! 它就是我们今天介绍的主人公 --- ImageCache 可能有些人对ImageCache还有些陌生,

  • 往MySQL中存储图片的方法

    1 介绍 在设计到数据库的开发中,难免要将图片或音频文件插入到数据库中的情况.一般来说,我们可以同过插入图片文件相应的存储位置,而不是文件本身,来避免直接向数据库里插入的麻烦.但有些时候,向MySQL中插入图片更加容易管理.那么在MySQL中该怎么存储呢? 参考资料[1]中有个相当清晰的例子,不过是基于MySQL图形界面的查询工具Query Brower的,你的机子上没有安装的话,可能得不到很好的理解.我在这里不在赘述,更详细的资料请看给出的链接吧. 还有,[1]中的例子其实只是向我们说明了Qu

  • iOS把图片缓存到本地的几种方法(总结)

    把图片缓存到本地,在很多场景都会用到,如果只是存储文件信息,那建一个plist文件,或者数据库就能很方便的解决问题,但是如果存储图片到沙盒就没那么方便了.这里简单介绍两种保存图片到沙盒的方法. 一.把图片转为base64的字符串存到数据库中或者plist文件中,然后用到的时候再取出来 //获取沙盒路径, NSString *path_sandox = NSHomeDirectory(); //创建一个存储plist文件的路径 NSString *newPath = [path_sandox st

  • Android实现从缓存中读取图片与异步加载功能类

    本文实例讲述了Android实现从缓存中读取图片与异步加载功能类.分享给大家供大家参考,具体如下: 在新浪微博的微博列表中的图片,为了加速其显示也为了加快程序的响应,可以参考该图片异步加载类实现. public class AsyncImageLoader { //SoftReference是软引用,是为了更好的为了系统回收变量 private HashMap<String, SoftReference<Drawable>> imageCache; public AsyncImag

  • iOS中获取系统相册中的图片实例

    本文介绍了iOS中获取系统相册中的图片,在很多应用中都能用到,可以获取单张图片,也可以同时获取多张图片,废话不多说了,看下面吧. 一.获取单张图片 思路: 1.利用UIImagePickerController可以从系统自带的App(照片\相机)中获得图片 2.设置代理,遵守代理协议 注意这个UIImagePickerController类比较特殊,需要遵守两个代理协议 @interface ViewController () <UIImagePickerControllerDelegate,

  • Android中Glide加载库的图片缓存配置究极指南

    零.选择Glide 为什么图片加载我首先推荐Glide? 图片加载框架用了不少,从afinal框架的afinalBitmap,Xutils的BitmapUtils,老牌框架universalImageLoader,著名开源组织square的picasso,google推荐的glide到FaceBook推出的fresco.这些我前前后后都体验过,那么面对这么多的框架,该如何选择呢?下面简单分析下我的看法. afinal和Xuils在github上作者已经停止维护了,开源社区最新的框架要属KJFra

  • iOS开发中使用UIScrollView实现无限循环的图片浏览器

    一.概述 UIKit框架中有大量的控件供开发者使用,在iOS开发中不仅可以直接使用这些控件还可以在这些控件的基础上进行扩展打造自己的控件.在这个系列中如果每个控件都介绍一遍确实没有必要,所谓授人以鱼不如授人以渔,这里会尽可能让大家明白其中的原理,找一些典型的控件进行说明,这样一来大家就可以触类旁通.今天我们主要来看一下UIScrollView的内容: UIView UIScrollView 实战--图片浏览器 二.UIView 在熟悉UIScrollView之前很有必要说一下UIView的内容.

  • iOS中实现图片自适应拉伸效果的方法

    前言 在Android中实现图片的拉伸特别特别简单,甚至不用写一行代码,直接使用.9图片进行划线即可.但是iOS就没这么简单了,比如对于下面的一张图片(原始尺寸:200*103): 我们不做任何处理,直接将它用作按钮的背景图片: // // ViewController.m // ChatBgTest // // Created by 李峰峰 on 2017/1/23. // Copyright © 2017年 李峰峰. All rights reserved. // #import "View

随机推荐