我想澄清一下。

假设我有以下代码:

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

这将在此功能调用中创建500万个自动发行的字符串。我期望这会保留这些对象,直到应用程序终止为止,因为我看到的唯一@autoreleasepool是将应用程序实例包装在Main.M.M. M.中。不过,情况并非如此。在此功能调用的结尾,看来他们都被释放了,并将其从内存中删除。

这个文件:

https://developer.apple.com/library/mac/documentation/cocoa/reference/foundation/classes/nsautoreleaseleasepool_class/reference/reference/reference/reference.html

指出:“应用程序套件在事件循环的每个周期开始时在主线程上创建一个自动发行池,并在末尾排干,从而在处理事件时释放生成的任何自我释放对象。”

这对我来说很有意义,但这在Uikit下,而不是应用程序套件。我的问题是,在这种情况下,Uikit/Cocoa Touch是否会做同样的事情,还是对我的对象有其他解释?

谢谢!

有帮助吗?

解决方案 2

是的,Uikit做同样的事情。系统创建的主线程自动发行池在每个运行循环周期的结尾都排出。最好不要在自己的代码中依靠这种确切的生命周期。如果您手动创建一个新线程(使用例如NSTHREAD),则负责在该线程上创建Autorelease池。

编辑:Rob的答案提供了有关ARC下行为的一些良好附加信息。总的来说,可以说,由于某些优化弧的能力,对象不太可能最终进入自动发行池。

其他提示

安德鲁回答了您的主要问题,是的,您的自动发行池将在主跑步循环的每个周期中排出。因此,在 viewDidLoad 当您屈服于主跑步循环时,可能会迅速耗尽。当然,他们“直到申请终止”。

但是我们应该小心:您显然假设这些对象被添加到自动发行池中。这一假设有一些警告:

  1. 在过去(ARC-MRC互操作性仍然需要),当从名称不始于的方法返回对象时 alloc, new, copy, , 或者 mutableCopy, ,这些对象会自动发行对象,只有在耗尽自动发行池(即屈服回运行循环时)时才会进行交易。

  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

以下代码将在Autorelease池中添加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,当它使用自动发行对象以及当变量掉落范围时明确释放时,并不总是很明显。您始终可以通过检查仪器中的行为来确认这一点。

顺便说一句,我对使用太多一般内存管理结论时会保持警惕 NSString 班级,因为它已经得到了高度优化,并且并不总是符合标准内存管理实践。

我假设,当您将新对象分配给用来保存对象的引用时 原始对象立即发布 (如果没有其他指向的话 - 裁判计数为零)使用弧和默认值 strong 如您的循环示例中的参考。

MyObject *object = [[MyObject alloc] init]; // obj1, ref count 1 because strong
object = [[MyObject alloc] init]; // obj2, ref count of obj1 should be 0
                                  // so obj1 gets released

苹果笔记入门 过渡到电弧发行说明

尝试停止考虑放置/发布通话的位置,然后考虑您的应用程序算法。考虑物体中的“强和弱”指针,对象所有权以及可能的保留周期。

听起来是 release 分配了新值,从clang中调用 Clang 3.4文档Objective-C自动参考计数(ARC)

评估作业操作员时会发生分配。语义基于资格的不同:

对于__ strong对象,首先保留了新的Pointee;其次,LVALUE装有原始语义。第三,新的Pointee用原始语义存储在LVALUE中。最后,旧的Pointee被释放了。这不是在原子上执行的;面对并发负载和存储,必须使用外部同步来使其安全。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top