ios - Autorelease 池和在 iOS 下调用 release 的时间

标签 ios objective-c cocoa-touch nsautoreleasepool

我想澄清一些事情。

假设我有以下代码:

- (void) viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  for (int i = 0; i < 5000000; i++) {
    NSString *s = [NSString stringWithFormat:@"Hello, %@!", @"World"];
  }
}

这将在此函数调用中创建 500 万个自动释放的字符串。我期望这会保留这些对象直到应用程序终止,因为我看到的唯一 @autoreleasepool 是将应用程序实例化包装在 main.m 中的那个。但事实并非如此。在此函数调用结束时,它们似乎都调用了它们的释放并从内存中删除。

本文档:

https://developer.apple.com/library/mac/documentation/cocoa/reference/foundation/Classes/NSAutoreleasePool_Class/Reference/Reference.html

声明“Application Kit 在事件循环的每个周期开始时在主线程上创建一个自动释放池,并在结束时将其排出,从而释放在处理事件时生成的任何自动释放对象。”

这对我来说很有意义,但这是在 UIKit 下,而不是在 Application Kit 下。我的问题是,在这种情况下,UIKit/Cocoa Touch 是否会做同样的事情,或者对于我的对象被释放是否有另一种解释?

谢谢!

最佳答案

Andrew 回答了您的主要问题,是的,您的自动释放池将在主运行循环的每个周期中耗尽。因此,当您回到主运行循环时,在 viewDidLoad 中创建的任何自动释放对象都可能会立即耗尽。他们肯定不会被保留“直到申请终止”。

但我们应该小心:您显然假设这些对象被添加到自动释放池中。对此假设的一些注意事项:

  1. 过去(ARC-MRC 互操作性仍然需要),当从名称不是以 alloc 开头的方法返回对象时,newcopymutableCopy,这些对象将自动释放对象,仅在自动释放池被耗尽时(即当您返回运行循环时)才释放对象。

  2. 但是 ARC 在最小化对自动释放池的需求方面变得更加聪明(参见 http://rentzsch.tumblr.com/post/75082194868/arcs-fast-autorelease ,其中讨论了 callerAcceptsFastAutorelease,现在称为 callerAcceptsOptimizedReturn 调用prepareOptimizedReturn), 所以你通常不会看到这种 autorelease 行为。因此,如果库和调用者都在使用 ARC,则对象可能不会放在自动释放池中,而是 ARC 会在不需要时巧妙地立即释放它们。

    对于当代的 ARC 项目,通常不需要自动释放池。但是在某些特殊情况下,仍然可以从使用自动释放池中受益。我将在下面概述其中一个案例。

考虑以下代码:

#import "ViewController.h"
#import <sys/kdebug_signpost.h>

typedef enum : NSUInteger {
    InnerLoop = 1,
    MainLoop = 2
} MySignPostCodes;

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        kdebug_signpost_start(MainLoop, 0, 0, 0, 1);
        for (int j = 0; j < 500; i++) {
            NSData *data = [NSData dataWithContentsOfURL:fileURL];
            UIImage *image = [[UIImage alloc] initWithData:data];
            NSLog(@"%p", NSStringFromCGSize(image.size));  // so it's not optimized out
            [NSThread sleepForTimeInterval:0.01];
        }
        kdebug_signpost_end(MainLoop, 0, 0, 0, 1);
    });
}

@end

以下代码将向自动释放池添加 500,000 个对象,只有当我返回运行循环时才会将其耗尽:

no pool

在这种情况下,您可以使用自动释放池来最小化高水位线:

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        kdebug_signpost_start(MainLoop, 0, 0, 0, 1);
        for (int j = 0; j < 5; j++) {
            @autoreleasepool {
                kdebug_signpost_start(InnerLoop, 0, 0, 0, 2);
                for (long i = 0; i < 100; i++) {
                    NSData *data = [NSData dataWithContentsOfURL:fileURL];
                    UIImage *image = [[UIImage alloc] initWithData:data];
                    NSLog(@"%p", NSStringFromCGSize(image.size));  // so it's not optimized out
                    [NSThread sleepForTimeInterval:0.01];
                }
                kdebug_signpost_end(InnerLoop, 0, 0, 0, 2);
            }
        }
        kdebug_signpost_end(MainLoop, 0, 0, 0, 1);
    });
}

@end

pool

最重要的是,对于 ARC,它何时使用自动释放对象以及何时在变量超出范围时显式释放它并不总是很明显。您始终可以通过检查 Instruments 中的行为来确认这一点。

顺便说一句,我对在使用 NSString 类时得出太多一般内存管理结论持谨慎态度,因为它已经过高度优化并且并不总是符合标准内存管理实践.

关于ios - Autorelease 池和在 iOS 下调用 release 的时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19840676/

相关文章:

ios - 如何制作像 Facebook 应用程序一样的登录屏幕?

具有动态高度的 iOS UIView 取决于内部元素的数量

ios - 在 AppseleratorStudio 上构建 iOS 时出现问题 [错误] : Invalid "--device-id" value ""

C# 代码到 Objective C 等效代码

ios - 有符号整数,算术左移

iphone - 通过 MKPolyline 获取中间点

objective-c - CoreText CopyFontsForRequest 收到 mig IPC 错误

iphone - 什么时候使用哪个? NSURLConnection 与较低级别的套接字 API

ios - 如何使用 ECSlidingViewController 知道菜单何时为 "open"

iphone - 我可以更改游戏中心成就的图像吗?