8

为什么 Objective-C命令行iOS程序的 Xcode 4.x 模板会添加@autoreleasepool {}part wrappingmain()的代码?请注意,这不会发生在OS X应用程序模板中。

为什么 OS X 应用程序不这样做呢?为什么不使用相同的方法?

最后,既然任何程序退出时都会释放所有内存,为什么所有这些都具有实际重要性?


或者换一种说法,省略命令行@autoreleasepool { ... }或iOS Objective -C 程序的实际后果是什么?main()

这两段代码编译并且似乎等效地工作:

1.

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        NSArray *array = @[@"Hello, world!"];
        NSLog(@"%@", array[0]);
    }
    return 0;
}

2.

int main(int argc, const char * argv[])
{
    NSArray *array = @[@"Hello, world!"];
    NSLog(@"%@", array[0]);
}

注意,我只关心 ARC 上下文中的解释。ARC 禁止显式使用autorelease.

4

4 回答 4

5

autorelease如果堆栈上没有自动释放池,则不起作用。

在objective-c中使用自动释放对象并不是真的必要(就像你在你的例子中所做的那样),所以理论上你可以省略它,但是大多数Apple框架确实大量使用自动释放对象。

通常,每个线程至少应该有一个自动释放池,否则使用任何 Obj-C 代码都是非常不安全的。在开始时设置一个自动释放池main是一个非常好的做法。

编辑:

在 ARC 中,虽然autorelease禁止显式调用,但autorelease调用仍然存在(由编译器添加)。这意味着需要一个自动释放池。

这与释放内存无关。自动释放池的存在是必要的。即使它从未被耗尽。

我猜 OS X 不会将自动释放池添加到模板中,因为程序员也可以使用垃圾收集器(尽管现在已弃用)。

编辑2:

刚刚创建了一个 OS X 项目,它就在@autoreleasepool那里。事实上,唯一没有它的模板是“Core Foundation”项目,它不是真正的 Obj-C,它是纯 C。

编辑3:(经过更多思考和谷歌搜索)

随着 ARC 的引入,自动释放池被重写。以前它们是框架功能,现在它们是语言(Obj-C)功能。它们的实现方式不同。现在似乎每个新线程都有一个隐式的自动释放池。using@autoreleasepool实际上不再在某个线程堆栈上创建新的自动释放池,它只是在隐式自动释放池中放置一个标记(这样您就可以在标记之后耗尽所有自动释放的内容)。@autoreleasepool这意味着在省略时无法创建示例触发警告或错误。

但是,这被认为是一个实现细节,因此将来可以轻松更改(或使用另一个编译器时!)。这就是为什么仍然为每个新线程设置一个很好的做法@autoreleasepool(例如,在-[NSThread detachWithSelector:..]文档中提到)。

于 2013-08-24T19:54:15.433 回答
3

在上面的代码示例中,您没有使用任何自动释放的对象。

但是,如果你正在做这样的事情:

NSString * somethingToSay = [NSString stringWithString:@"this is an autoreleased string, believe it or not"];
NSLog(@"%@", somethingToSay);
return 0;

" @autoreleasepool" 位实际上会更有用一些。

查看 Apple 的 autoreleasepool 文档,他们说:

在自动释放池块结束时,在块内收到自动释放消息的对象会收到释放消息——对象每次在块内收到自动释放消息时都会收到释放消息。

因此,当您有很多自动释放的对象(并且有很多可以创建为 NSArray、NSDictionary、NSString 等的对象)时,这些自动释放池有助于保持内存释放。

应用程序模板@autoreleasepool在 main.m 中包含“”,因为在 iPhone 或 iPad 等设备上,内存使用至关重要,我们希望确保在应用程序退出“ UIApplicationMain”时正确释放所有内存(不太可能,因为大多数人让他们的 iPhone 应用程序一直运行直到重新启动,但这可能会发生)。

于 2013-08-24T19:28:36.960 回答
2

你需要它,因为文档说你需要它。这应该足够了,相信文档:

通常,您不需要创建自己的自动释放池块,但在某些情况下,您必须这样做或者这样做是有益的。

...

Cocoa 总是希望代码在自动释放池块中执行,否则自动释放的对象不会被释放并且您的应用程序会泄漏内存。(如果您在自动释放池块之外发送自动释放消息,Cocoa 会记录一个合适的错误消息。)

...但是,在三种情况下您可能会使用自己的自动释放池块:

  • 如果您正在编写不基于 UI 框架的程序,例如命令行工具。
  • ...
  • ...

...

如果您正在编写纯基础程序或分离线程,则需要创建自己的自动释放池块。

如果没有自动释放池......那么大多数事情仍然可以正常工作,但这是“大多数”事情,而不是“所有”事情。他们只陈述了两个原因,但是可能还有其他原因没有记录在案,所有这些行为都可能在未来的任何时候发生变化。

如果您遵循推荐的最佳实践,那么您的代码应该在 20 年后仍能完美运行,但如果您不这样做,它可能会在操作系统的下一次 xxx 更新开始每 50 次执行中随机崩溃一次。

于 2013-08-25T12:14:18.070 回答
1

苹果文档中的这句话可能会有所帮助:

如果您的分离线程不进行 Cocoa 调用,则不需要使用自动释放池块。

它可以外推到所有线程,甚至是主线程(“main”所在的位置)。

于 2014-07-20T12:04:51.147 回答