了解 NSAutoreleasePool
Posted
技术标签:
【中文标题】了解 NSAutoreleasePool【英文标题】:Understanding NSAutoreleasePool 【发布时间】:2014-12-06 06:16:34 【问题描述】:我有一个应用程序在 iPhone 4s 上使用相机时收到内存警告。我在使用之前缩放图像。
+ (UIImage*)simpleImageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize
// Create a graphics image context
UIGraphicsBeginImageContext(newSize);
// Tell the old image to draw in this new context, with the desired
// new size
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
// Get the new image from the context
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
// End the context
UIGraphicsEndImageContext();
// Return the new image.
return newImage;
我读到你应该从这里使用 NSAutoreleasePool http://wiresareobsolete.com/2010/08/uiimagepickercontroller/
所以我修改了这样的代码:
+ (UIImage*)simpleImageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize
//http://wiresareobsolete.com/2010/08/uiimagepickercontroller/
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Create a graphics image context
UIGraphicsBeginImageContext(newSize);
// Tell the old image to draw in this new context, with the desired
// new size
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
// Get the new image from the context
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
// End the context
UIGraphicsEndImageContext();
[newImage retain];
[pool release];
// Return the new image.
return newImage;
内存警告消失了。我没有在单独的线程上调用它。 这个 NSAutoreleasePool 在做什么?我只是不明白。
我可以在本地 NSAutoreleasePool 中保留一个对象吗?
NSAutoreleasePool释放后我可以使用保留的对象吗?
重要的问题:NSAutoreleasePool 的这种特定用法如何帮助我的应用程序占用内存,使其不会收到内存警告?
【问题讨论】:
现代形式是@autoreleasepool
... 通常情况下,当池耗尽时会释放具有零保留计数的自动释放对象,因此在耗尽池时强制释放。显式的retain
应该生成警告/错误...
newImage 的显式保留不会导致警告/错误。
你没有使用 ARC?
这些天你需要把这些信息放在前面。无论如何,我不是您正在使用的 API 的专家,但我想 UIGraphicsEndImageContext 会将一些保留计数降至零,并且显式池耗尽会立即释放这些对象。没有池,释放不会立即发生。
无论您是否使用 ARC,您都应该使用 @autoreleasepool
块而不是 NSAutoreleasePool
对象。使用NSAutoreleasePool
对象确实已经过时了。
【参考方案1】:
首先,您应该使用@autoreleasepool
块而不是NSAutoreleasePool
对象;后者已经过时了。前者更好,因为它会在您离开块的范围时自动排空池,而无需您自己显式地这样做。
自动释放池仅记住在其范围内“自动释放”的内容,并在池耗尽时释放它们。 “自动释放”一个对象就像释放它,但延迟了;从内存管理的角度来看,它采用您拥有的强引用并将其“传输”到池中,因此池现在对对象具有强引用,而您没有。在 ARC 之前,如果一个方法(其名称不是以 new
或 copy
开头)需要返回一个在函数期间创建的对象,它几乎必须被自动释放。通过将它放在池中,它保证对象将被调用函数接收到。在您的代码中,UIGraphicsGetImageFromCurrentImageContext()
可能会返回一个自动释放的对象。
自动释放池仅在池结束时排空。池中的对象在池期间保持活动状态(因为它们实际上是由池“拥有”的)。这意味着如果池持续很长时间并且很多东西在其上被自动释放,那么很多对象被阻止被释放,这可能很糟糕。
例如,假设您的函数运行一次会自动释放一个对象(即UIGraphicsGetImageFromCurrentImageContext()
返回的对象)。然后,如果你在一个循环中运行你的函数 100,000 次,那么 100,000 个对象会留在内存中,因为它们被放到了同一个池中,还没有机会耗尽。但是,如果您在循环的每次迭代中放入另一个级别的池,它将在每次迭代结束时耗尽,从而防止对象堆积。
至于您的第二段代码,在您的simpleImageWithImage:
方法中放置一个自动释放池确实会捕获来自UIGraphicsGetImageFromCurrentImageContext()
的自动释放。但是,您还有另一个问题,因为为了从simpleImageWithImage:
方法本身返回图像对象,您需要自动释放它!
您所写的方法违反了内存管理规则。您的方法返回一个保留对象,调用者必须记住释放该对象。但是,调用者不知道基于名称。根据命名约定,唯一可以返回保留实例的方法是名称以alloc
、retain
、new
、copy
和mutableCopy
开头的方法。您的方法不以其中任何一个开头,因此它必须返回一个非保留实例。由于您拥有该对象(通过保留它),您必须进行平衡释放或自动释放。你不能释放它,因为它可能会导致对象被释放,因为没有其他强引用它,所以你只能自动释放它。
但是如果你要再次自动释放它,你在这里添加一个自动释放池并没有完成任何事情,除非在drawInRect:
或UIGraphicsGetImageFromCurrentImageContext()
内部它们自动释放了很多东西。
【讨论】:
【参考方案2】:自己创建的NSAutoreleasePool只是调用NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
到[pool release];
区域内的autorelease对象的release方法,
这里
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
newImage
是一个自动释放对象,[newImage release]
会在[pool release];
之后被调用
如果不是[newImage retain];
newImage
将在[pool release];
之后解除分配
-
您可以在本地 NSAutoreleasePool 中保留对象。
您可以在 NSAutoreleasePool 释放后使用保留的对象。
使用
NSAutoreleasePool
释放临时内存。
【讨论】:
UIGraphicsGetImageFromCurrentImageContext 是否会创建一些标记为自动释放的大对象?否则我不明白这实际上是如何减少任何内存使用的。是不是因为 NSAutoreleasePool 被释放,所以当我稍后在返回的 newImage 上调用 release 时,它会立即释放内存,而不是仅仅减少它的计数以在以后释放内存? 应该是系统镜像方法中有自动释放对象,我没看过UIGraphicsGetImageFromCurrentImageContext
源码,无法确认。是的,你对 newImage 所做的,不会帮助释放内存,但有助于保持 newImage 可以在 autoreleasePool 释放后使用。
对我问题的最后一部分有任何想法吗?为什么这样做会导致 ios 停止发出内存警告?当 AutoreleasePool (ARP) 达到某个高水位标记时是否会发出内存警告?如果 ARP 中有对象使用 X 内存量并且 X 大于某个值,那么 iOS 会发送内存警告吗?我将简化一个例子。假设最大值为 100 个内存单位。如果我有 ARP 1 并且它有使用 99 个内存单位的对象并且我添加了另一个对象,那么 iOS 会发送警告吗?如果我有两个池 ARP 1 和 ARP 2,每个池的对象使用 99 个内存单元,那么没有警告?
清空池只是强制立即释放具有零保留计数的对象,而不是等待主线程在运行循环中运行一圈。以上是关于了解 NSAutoreleasePool的主要内容,如果未能解决你的问题,请参考以下文章