[ios2]iOS 图片与内存 【转】

时间:2023-03-08 15:43:50

第一种解决方法:选择适当的加载方式

在程序的开发过程中,经常会用到很多的图片,适当的选择加载图片的方式就显得格外的重要,如果选择不得当,很容易造成内存吃紧而引起程序的崩溃。

这里介绍一下几种常见的加载方式:

用UIImage加载图像的方法很多,最常用的是下面两种:

一、用imageNamed函数

引用
[UIImage imageNamed:ImageName];

二、用NSData的方式加载,例如:

引用
NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:extension]; 
NSData *image = [NSData dataWithContentsOfFile:filePath];
[UIImage imageWithData:image];

三,使用[UIImage imageWithContentOfFile:] 或者[image initWithContentOfFile:]

引用
NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@"图片扩展名"]; 
[UIImage imageWithContentsOfFile:aImagePath];

其实本质上和方法二是一样的

由于第一种方式要写的代码比较少,可能比较多人利用imageNamed的方式加载图像。其实这两种加载方式都有各自的特点。

1)用imageNamed的方式加载时,系统会把图像Cache到内存。如果图像比较大,或者图像比较多,用这种方式会消耗很大的内存,而且释放图像的
内存是一件相对来说比较麻烦的事情。例如:如果利用imageNamed的方式加载图像到一个动态数组NSMutableArray,然后将将数组赋予一
个UIView的对象的animationImages进行逐帧动画,那么这将会很有可能造成内存泄露。并且释放图像所占据的内存也不会那么简单。但是利
用imageNamed加载图像也有自己的优势。对于同一个图像系统只会把它Cache到内存一次,这对于图像的重复利用是非常有优势的。例如:你需要在
一个TableView里重复加载同样一个图标,那么用imageNamed加载图像,系统会把那个图标Cache到内存,在Table里每次利用那个图
像的时候,只会把图片指针指向同一块内存。这种情况使用imageNamed加载图像就会变得非常有效。

2)利用NSData或imageWithContentOfFile方式加载时,图像会被系统以数据方式加载到程序。当你不需要重用该图像,或者你需要
将图像以数据方式存储到数据库,又或者你要通过网络下载一个很大的图像时,请尽量使用imageWithData的方式加载图像。

无论用哪种方式加载图像,图像使用结束后,一定要记得显示释放内存。

第二种解决方法:使用urlloader

近排做项目研究发现,如果直接用loader加载图片,然后在用bitmap生成图片,内存占用巨大,如果图片多了,那么放到移动设备肯定就会直接泪奔。

研究发现,如果用 urlloader 来加载图片,获取图片的2进制数据,保存起来,内存占用很小。
我加载了50张图片,大小为1024*576像素,发现内存只用了30-40M,比保存bitmapdata小得很多很多,如果放在 移动端,那么这内存
还是可以接受的,除非你真的用1000张图片,这是你就得分布加载了······

那么如何读取呢,唔错,我们可以用loader的 loadbytes方法来读取,但是要注意一点,在移动端,必须添加一个loaderContext,并且设置 loaderContext.allowLoadBytesCodeExecution = true;才能在移动端加载bytes图片

第三种解决方法:官方解释

Images and Memory Management

In low-memory
situations, image data may be purged from a UIImage object to free up
memory on the system. This purging behavior affects only the image data
stored internally by the UIImage object and not the
object itself. When you attempt to draw an image whose data has been
purged, the image object automatically reloads the data from its
original file. This extra load step, however, may incur a small
performance penalty.

You should avoid
creating UIImage objects that are greater than 1024 x 1024 in size.
Besides the large amount of memory such an image would consume, you may
run into problems when using the image as a texture
in OpenGL ES or when drawing the image to a view or layer. This size
restriction does not apply if you are performing code-based
manipulations, such as resizing an image larger than 1024 x 1024 pixels
by drawing it to a bitmap-backed graphics context. In fact,
you may need to resize an image in this manner (or break it into
several smaller images) in order to draw it to one of your views.

添加一段官方的SDK内容,官方里面也说,对于UIImage的对象,当在低内存的情况,会被清空。释放出内存。当下次使用的时候从文件中加载。如果我们在一个view使用超过文中提到的 storedspace(这块应该不是简单的内存)上限时候,可能就会爆内存警报

imageNamed:

Returns the image object associated with the specified filename.
+ (UIImage *)imageNamed:(NSString *)name
Parameters

name

The name of the
file. If this is the first time the image is being loaded, the method
looks for an image with the specified name in the application’s main
bundle.

Return Value

The image object for the specified file, or nil if the method could not find the specified image.
Discussion

This method looks in
the system caches for an image object with the specified name and
returns that object if it exists. If a matching image object is not
already in the cache, this method loads the image data
from the specified file, caches it, and then returns the resulting
object.

On a device running
iOS 4 or later, the behavior is identical if the device’s screen has a
scale of 1.0. If the screen has a scale of 2.0, this method first
searches for an image file with the same filename
with an @2x suffix appended to it. For example, if the file’s name is
button, it first searches for button@2x. If it finds a 2x, it loads that
image and sets the scale property of the returned UIImage object to
2.0. Otherwise, it loads the unmodified filename
and sets the scale property to 1.0. See iOS Application Programming
Guide for more information on supporting images with different scale
factors.
Special Considerations

On iOS 4 and later, if
the file is in PNG format, it is not necessary to specify the .PNG
filename extension. Prior to iOS 4, you must specify the filename
extension.

If a matching image
object is not already in the cache, this method loads the image data
from the specified file, caches it, and then returns the resulting
object.从文档这段从可以判断,IOS可以用内存,跟我一般见到的内存警报应该是无关的。从测试案例3中如果一次加载到cache中
图片上限超过cache大小,但是low-memory situations, image data may be purged from a
UIImage object to free up memory on the system. 释放不够快话,就是出现我们一般见到的 内存警报。

如果一定要一次在一个view加载超过7Mb的图片,
initWithContentsOfFile:

Initializes and returns the image object with the contents of the specified file.
- (id)initWithContentsOfFile:(NSString *)path
Parameters

path

The path to the file. This path should include the filename extension that identifies the type of the image data.

Return Value

An initialized UIImage object, or nil if the method could not find the file or initialize the image from its contents.
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.
Availability

Available in iOS 2.0 and later.

Declared In
UIImage.h
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.
Availability
上面这句话,- (id)initWithContentsOfFile:(NSString *)path 加载到内存中,而且添加标记。不是在cache中如果下次如果释放,下次再次使用的话。会再次加载,但是它也是放在一个特殊的区域。不是在原始的区域。

第三种方法:

程序里经常会加载一些UI
图片,当取souce里的图片时无外乎用类方法和实例方法,在这里推荐用实例方法即用alloc并且手动释放的方式,图片小且数量不大时影响不大,若大量
图片可以看到对内存的影响,用类方法(不手动释放内存,而是仍到自动释放池里让系统自动释放,实际却是不知道**何时释放)则会占用大量内存。

以下是发现的转换成NSData进行加载方法:

[plain] view
plain
copy
  1. NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:extension];
  2. NSData *image = [NSData dataWithContentsOfFile:filePath];
  3. [UIImage imageWithData:image];

这里的image用alloc创建并手动释放更具效率,以下是常用方法:

[plain] view
plain
copy
  1. UIImageView *bacImageView = [[UIImageViewalloc]initWithFrame:CGRectMake(0,0,320,460)];
  2. SString *bacString = [[NSBundlemainBundle]pathForResource:@"testImage"ofType:@"png"];
  3. UIImage *bacImage = [[UIImagealloc]initWithContentsOfFile:bacString];
  4. [bacImageViewsetImage:bacImage];
  5. [self.viewaddSubview:bacImageView];
  6. [bacImageViewrelease];
  7. [bacImagerelease];

UIImageView显示选取的图片,加载完成后直接手动释放。

PS:这里做下标记,功能用两种方法都可实现时尽量用实例方法去实现,手动释放内存,少用类方法,使程序减少负荷,少占用内存,加快运行速度。