最近在做一个iOS手机项目的时候,遇到一个奇怪的问题,这里跟大家分享一下。

一、问题重现

1、启动App后,通过http请求下载了一个1.jpg文件到Cache目录下,下载成功之后,将图片显示在界面上;(图1)

2、此时杀掉进程,再次启动App后,图片可以正常显示,然后点击一个按钮删除刚刚下载的图片;(图2)

3、此时,将App压后台,再唤起,原来显示的图片消失了!!!(图3)

图1:                图2:               图3:

        

这里我们先贴一下代码,用代码来说明问题:

 @implementation ViewController

 - (void)viewDidLoad {
[super viewDidLoad]; _imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
_imageView.backgroundColor = [UIColor blackColor];
_imageView.contentMode = UIViewContentModeScaleToFill;
[self.view addSubview:_imageView]; _clearButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_clearButton.frame = CGRectMake(, , , );
_clearButton.center = self.view.center;
[_clearButton setTitle:@"清理缓存" forState:UIControlStateNormal];
[_clearButton addTarget:self action:@selector(clearCache) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_clearButton];
} - (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated]; [self showImage];
} - (NSString *)imagePath
{
NSArray *pathcaches=NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [pathcaches objectAtIndex:];
return [cacheDirectory stringByAppendingString:@"1.jpg"];
} // 显示图片
- (void)showImage
{
NSString *imageUrl = @"http://pic2.desk.chinaz.com/file/201203/6/chuangyisjbz1_p.jpg";
NSURL *imageURL = [NSURL URLWithString:imageUrl];
NSString *imagePath = [self imagePath]; UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
if (image) {
// 第二次启动App,缓存文件存在时,通过[UIImage imageWithContentsOfFile:]初始化
_imageView.image = image;
} else {
// 第一次启动APP,下载图片成功后,通过[UIImage imageWithData:]初始化
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
NSData *data = [NSData dataWithContentsOfURL:imageURL];
if (data) {
dispatch_async(dispatch_get_main_queue(), ^{
[data writeToFile:imagePath atomically:YES];
_imageView.image = [UIImage imageWithData:data];
});
}
});
}
} // 清理缓存
- (void)clearCache
{
[[NSFileManager defaultManager] removeItemAtPath:[self imagePath] error:nil];
} @end

二、原因分析

  产生这个原因的核心原因在于UIImage的初始化方法。

1、直接使用文件初始化图片

UIImage *image = [UIImage imageWithContentsOfFile:imagePath]

当杀掉进程,第二次启动App的时候,缓存文件存在,则采用上面的方式初始化图片;清除缓存,压后台,再唤起,注意观察Console区域,会发现如下提示信息:

Mar  9 20:52:02  Demo[38525] <Error>: ImageIO: CGImageReadCreateDataWithMappedFile  'open' failed '/Users/yanzhi/Library/Developer/CoreSimulator/Devices/30EE295B-C260-4A5E-9446-362D05D50C0B/data/Containers/Data/Application/94D96217-D6DB-4BFC-BFD7-60FB66EA7A9E/Library/Caches1.jpg'
error = 2 (No such file or directory)

这里,需要注意,压后台,再唤起,我们并没有再次执行imageView.image = image的操作,但是为什么图片就没有了呢?同时比较奇怪的是,为什么会输出一个ImageIO错误。

注意一下关于[UIImage initWithContentsOfFile:]方法的官方文档说明:

- (instancetype)initWithContentsOfFile:(NSString *)path
Discussion
This method loads the image data into memory and marks it as purgeable. If the data is purged and needs to be reloaded, the image object loads that data again from the specified path.
这个方法加载图片数据到内存中并将其标记为“可清除”。如果内存中图片被清除,需要重新加载时,这个Image对象需要再次从指定的path中加载图像数据。

解释一下,[UIImage imageWithContentsOfFile:]没有上面的说明信息,仅仅在[UIImage initWithContentsOfFile:]方法中有这段说明。

正如上面文档说明,当我们压后台的时候,内存中的Image对象是可以清除,于是就被系统回收掉该内存空间;再次唤起的时候,这个Image对象会尝试重新加载该Path所指向的文件;但是该文件已经被删除掉,因此系统在重新加载图片的时候,就出现了ImageIO的错误数据,于是界面也无法再次展示该图片。

我们可以理解为:使用initWithContentsOfFile的时候,系统为我们的Image对象和Path指向的文件做了一个映射Map,当Image对象被清理掉后,需要再次使用该Image对象时,会自动从Path指向的文件中去读取数据。

因此,当大家使用initWithContentsOfFile或imageWithContentsOfFile去初始化图片的时候,切记注意你的图片文件是否可能被清理掉!

2、使用NSData转换初始化图片

NSData *data = [NSData dataWithContentsOfFile:path];

UIImage *image = [UIImage imageWithData:data];

当使用NSData作为一个中间对象来转换的时候,如果path文件被删除了,但是对应的data对象并不会被清理掉,始终会在内存中,那么由此生成Image对象也不会被清理。

大家可以做一个实验,使用这两个方法替换上方代码片段中的[UIImage imageWithContentsOfFile:]方法,重新验证一下,你会发现同样的场景,图片始终会正常显示。

三、总结

1、initWithContentsOfFile和imageWithContentsOfFile生成的Image对象,用来一次性展示,不可被缓存;Image对象可能被系统自动清理掉,并由系统自动加载;

2、如果需要缓存该Image对象,慎重选择UIImage的初始化方法。

【原】[UIImage imageWithContentsOfFile:]引发的图片无法显示的问题的更多相关文章

  1. swift UIImage加载远程图片和圆角矩形

    UIImage这个对象是swift中的图像类,可以使用UIImageView加载显示到View上. 以下是UIImage的构造函数: init(named name: String!) -> U ...

  2. UIImage imageNamed和UIImage imageWithContentsOfFile区别

    UIImage imageNamed和 [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:imageNam ...

  3. 我的Android最佳实践之—— ImageView中图片拉伸显示

    通过设置android:scaleType="fitXY"使得图片拉伸显示.补充:scaleType的属性有matrix(默认).center.centerCrop.centerI ...

  4. 解决django关于图片无法显示的问题

    http://python.usyiyi.cn/django/index.html http://m.blog.csdn.net/blog/qingyuanluofeng/44877399 http: ...

  5. background属性冲突导致的部分浏览器背景图片不显示问题

    前几天在项目中遇到了一个让人摸不着头脑的bug,测试说页面显示有点问题并发了截图, 正常的显示状态是这样 首先我自信地用自己的手机检查了一下,没有问题,问清楚后得知是UC浏览器中出现的,UC的内核是u ...

  6. 使用MailKit发送带有内嵌图片的邮件且图片不显示成附件

    使用MailKit发送带有内嵌图片的邮件且图片不显示成附件 参考文章:MailKit---发送邮件 注意 在邮件客户端中是否显示内嵌图片为附件依据不同邮件有所不同,暂经测试Outlook和qq不显示为 ...

  7. CSS3鼠标悬停图片上浮显示描述代码

    效果:http://hovertree.com/texiao/css3/20/ 效果图: 代码如下: <!doctype html> <html lang="zh" ...

  8. 第3月30天 UIImage imageWithContentsOfFile卡顿 Can't add self as subview MPMoviePlayerControlle rcrash

    1. UIImage imageWithContentsOfFile卡顿 [[UIImage alloc] initWithContentsOfFile 卡顿 2.uitableview scroll ...

  9. IE浏览器下一些图片无法显示的(在其他非ie浏览器能够显示)解决办法

    因为要完成本学期老师布置得网页作业,做完的时候进行网页的兼容性测试,chrome和360都没有问题. ie下却有两个图片无法显示. 一般这种情况代码是没有什么问题的,一般是图片本身的问题, 当我用Ps ...

随机推荐

  1. 73. Set Matrix Zeroes (Array)

    Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place. Follow ...

  2. OGNL入门

    ------------------siwuxie095 OGNL 入门 1.OGNL 支持对象方法调用,即 objName.methodName() 如:使用 OGNL 表达式 + Struts2 ...

  3. c++ stringstream的使用

    stringstream ss;//一次创建多次使用,需要进行clear()操作清除流状态标记 int i=0; while (i<3) { ss<<"21"; ...

  4. 动态执行SQL语句,接收返回值

    一.exec和sp_executesql介绍 当需要根据外部输入的参数来决定要执行的SQL语句时,常常需要动态来构造SQL查询语句.比如,一个比较通用的分页存储过程,可能需要传入表名,字段,过滤条件, ...

  5. Go基本数据类型

    Go基本数据类型 第一部分:基本数据类型和操作符 1. 文件名&关键字&标识符 1. 所有go源码以.go结尾 2. 标识符以字母或下划线开头,大小写敏感,比如: a. boy  b. ...

  6. CentOS 7 装好系统一些优化

    1.禁用SELINUX vi /etc/sysconfig/selinux  设置为disabled 2.同步时间*/20 * * * * /usr/sbin/ntpdate pool.ntp.org ...

  7. win10自带虚拟机安装CentOS7系统(转)

    出处:http://blog.csdn.net/bimabushihaodongxi/article/details/53677490 话说工欲善其事,必先利其器,在我准备学习Linux之前先要完成l ...

  8. UVa 12186 Another Crisis (DP)

    题意:有一个老板和n个员工,除了老板每个员工都有唯一的上司,老板编号为0,员工们为1-n,工人(没有下属的员工),要交一份请愿书, 但是不能跨级,当一个不是工人的员工接受到直系下属不少于T%的签字时, ...

  9. mongodb-win32-i386-3.0.6 使用常见错误

    1.Error parsing YAML config file: yaml-cpp: error at line 3, column 28: unknown escape character: m ...

  10. Thrift编译错误('::malloc' has not been declared)

    问题版本:0.9.0 make[4]: Entering directory `/tmp/X/thrift-0.9.0/lib/cpp' /bin/sh ../../libtool  --tag=CX ...