了解 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 之前,如果一个方法(其名称不是以 newcopy 开头)需要返回一个在函数期间创建的对象,它几乎必须被自动释放。通过将它放在池中,它保证对象将被调用函数接收到。在您的代码中,UIGraphicsGetImageFromCurrentImageContext() 可能会返回一个自动释放的对象。

自动释放池仅在池结束时排空。池中的对象在池期间保持活动状态(因为它们实际上是由池“拥有”的)。这意味着如果池持续很长时间并且很多东西在其上被自动释放,那么很多对象被阻止被释放,这可能很糟糕。

例如,假设您的函数运行一次会自动释放一个对象(即UIGraphicsGetImageFromCurrentImageContext() 返回的对象)。然后,如果你在一个循环中运行你的函数 100,000 次,那么 100,000 个对象会留在内存中,因为它们被放到了同一个池中,还没有机会耗尽。但是,如果您在循环的每次迭代中放入另一个级别的池,它将在每次迭代结束时耗尽,从而防止对象堆积。

至于您的第二段代码,在您的simpleImageWithImage: 方法中放置一个自动释放池确实会捕获来自UIGraphicsGetImageFromCurrentImageContext() 的自动释放。但是,您还有另一个问题,因为为了从simpleImageWithImage: 方法本身返回图像对象,您需要自动释放它

您所写的方法违反了内存管理规则。您的方法返回一个保留对象,调用者必须记住释放该对象。但是,调用者不知道基于名称。根据命名约定,唯一可以返回保留实例的方法是名称以allocretainnewcopymutableCopy 开头的方法。您的方法不以其中任何一个开头,因此它必须返回一个非保留实例。由于您拥有该对象(通过保留它),您必须进行平衡释放或自动释放。你不能释放它,因为它可能会导致对象被释放,因为没有其他强引用它,所以你只能自动释放它。

但是如果你要再次自动释放它,你在这里添加一个自动释放池并没有完成任何事情,除非在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的主要内容,如果未能解决你的问题,请参考以下文章

了解了解grpc

数据库 了解知识

深入了解ASP.NET运行内幕

了解GStreamer

深入了解Java的String

Vue UI 了解一下