在Swift程序中是否需要使用自动释放池?

113

这个WWDC14演示文稿的第17页中,它说:

使用Objective-C?还需要管理自动释放池
autoreleasepool { /* 代码 */ }

这是什么意思?这是否意味着如果我的代码库中没有任何Objective-C文件,则autoreleasepool {}是不必要的?

一个相关问题的答案中,有一个例子说明了autoreleasepool可以很有用:

- (void)useALoadOfNumbers {
    for (int j = 0; j < 10000; ++j) {
        @autoreleasepool {
            for (int i = 0; i < 10000; ++i) {
                NSNumber *number = [NSNumber numberWithInt:(i+j)];
                NSLog(@"number = %p", number);
            }
        }
    }
}

如果上面的代码被转换成Swift时autoreleasepool被省略,那么Swift是否足够聪明以知道在第一个}之后应该释放number变量(就像其他一些语言所做的那样)?


1
在Swift中似乎没有关于autoreleasepool的文档。我已经扩展了您的问题并在开发者论坛上提出了它。 - Aaron Brager
3个回答

248

autoreleasepool 模式在 Swift 中用于返回autorelease对象(由您的 Objective-C 代码创建或使用 Cocoa 类)。Swift 中的 autorelease 模式的功能类似于 Objective-C。例如,请考虑以下方法的 Swift 实现(实例化 NSImage/UIImage 对象):

func useManyImages() {
    let filename = pathForResourceInBundle
    
    for _ in 0 ..< 5 {
        autoreleasepool {
            for _ in 0 ..< 1000 {
                let image = NSImage(contentsOfFile: filename)
            }
        }
    }
}

如果你在Instruments中运行它,你会看到一个带有5个小山丘的分配图(因为外部for循环),如下所示:

with autoreleasepool

但是如果不使用autorelease池,你会发现峰值内存使用率更高:

without autoreleasepool

autoreleasepool可以让你显式地管理Swift中自动释放对象的时间,就像你在Objective-C中所能做的一样。

注意:当处理Swift本机对象时,通常不会收到自动释放对象。这就是为什么演示提到“只有在与Objective-C一起工作时”需要此功能的警告,尽管我希望苹果在这一点上更加清楚。但如果你正在处理Objective-C对象(包括Cocoa类),它们可能是自动释放对象,在这种情况下,这种Swift实现的Objective-C@autoreleasepool模式仍然很有用。


2
在所有这些问题上,您可以编写自己的类,并在deinit中执行println,这样就可以轻松验证对象何时被释放。或者使用Instruments观察它。回答您的问题,Swift对象似乎是带有+1保留计数从函数返回的(不是自动释放对象),并且调用者将无缝地从那一点开始管理所有权(例如,如果返回的对象超出作用域,则立即释放,而不是放置在autorelease池中)。 - Rob
3
autorelease pool与内存泄漏关系不大。内存泄漏是由于未释放对象所造成的。相反,autorelease pool只是一组对象的集合,这些对象在池被清空之前都不会被释放。池并不控制何时释放对象,而仅仅控制释放的时间。对于地图视图的重新映射,你不能控制它的缓存行为(会使用内存,但不会真正泄漏),也无法处理真正的内存泄漏问题(尽管UIKit历史上出现过一些零散且不重要的泄漏问题)。 - Rob
3
@马特 是的,我看到了类似的行为。因此,我用NSImage/UIImage对象重复了我的练习,并更加一致地表现出了这个问题(说实话,这更是问题的常见例子,因为峰值内存使用率通常只在处理较大对象时成为问题;一个实际的例子可能是调整大小一堆图像的例程)。我还通过调用显式创建自动释放对象的Objective-C代码来复现了这种行为。别误会:我认为Swift中我们不像Objective-C那样经常需要自动释放池,但它仍然有一定的作用。 - Rob
1
我找到了一个可行的例子!只需反复调用NSBundle的pathForResource:ofType:即可。 - matt
1
我的 pathForResource:ofType:示例在Xcode 6.3 / Swift 1.2中不再起作用。 :) - matt
显示剩余11条评论

5

如果您在等效的 Objective-C 代码中使用它,则应该在 Swift 中使用它。

Swift 是否会聪明到知道 number 变量应在第一个 } 后释放?

只有当 Objective-C 这样做时才会这样。两者都遵循 Cocoa 内存管理规则。

当然,ARC 知道 number 在循环迭代结束时超出了作用域,并且如果它保留了它,就会在那里释放它。但是,这并不告诉您对象是否已自动释放,因为 -[NSNumber numberWithInt:] 可能已返回自动释放的实例或未释放的实例。 您无法知道,因为您无法访问 -[NSNumber numberWithInt:] 的源代码。


1
如果Swift在这方面的行为与Objective-C相同,那么为什么演示文稿特别提到“使用Objective-C”呢? - Ethan
12
看起来原生的Swift对象不是自动释放对象,因此autoreleasepool结构是完全不必要的。但是如果您的Swift代码正在处理Objective-C对象(包括Cocoa对象),那么这些对象遵循自动释放模式,因此autoreleasepool结构就变得有用了。 - Rob
我理解“自动释放池允许您在Swift中显式管理自动释放对象何时被释放”,但为什么我要这样做呢?为什么编译器不能为我完成这个任务?我不得不添加自己的自动释放池,以防止在大量字符串操作的紧密循环中VM崩溃。对我来说,应该在哪里添加它是显而易见的,并且它完美地工作了。为什么编译器不能做到这一点?编译器能否变得更聪明,做好这个工作? - vonlost

0

@autoreleasepool 可用于 Objective-C 和 Swift 代码中,确保与依赖于 autorelease 的 Objective-C 代码一起使用。

[底层实现]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接