【问题标题】:Is it necessary to use autoreleasepool in a Swift program?是否有必要在 Swift 程序中使用 autoreleasepool?
【发布时间】:2019-04-25 00:59:15
【问题描述】:

this WWDC14 presentation 第 17 页上写着

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

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

an answer of a related question 中,有一个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 变量应该在第一个 } 之后被释放(就像其他一些语言一样)?

【问题讨论】:

标签: memory-management swift


【解决方案1】:

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 循环),如下所示:

但是如果你在没有自动释放池的情况下这样做,你会发现峰值内存使用率更高:

autoreleasepool 允许您在 Swift 中明确管理何时释放自动释放对象,就像在 Objective-C 中一样。

注意:在处理 Swift 原生对象时,您通常不会收到自动释放对象。这就是为什么演示文稿提到只有在“使用 Objective-C”时才需要这个的警告,尽管我希望 Apple 在这一点上更清楚。但是,如果您处理的是 Objective-C 对象(包括 Cocoa 类),它们可能是自动释放对象,在这种情况下,Objective-C @autoreleasepool 模式的这种 Swift 版本仍然有用。

【讨论】:

  • 在所有这些问题上,您都可以编写自己的类,并让它在deinit 中执行println,并且很容易准确地验证对象何时被释放。或者在 Instruments 中观察它。在回答您的问题时,似乎 Swift 对象是从具有 +1 保留计数(不是自动释放对象)的函数返回的,并且调用者将从该点无缝管理所有权(例如,如果以及当返回的对象超出范围时,它会立即被释放,而不是放在自动释放池中)。
  • @StevenHernandez 自动释放池与泄漏几乎没有关系。泄漏是由未释放的对象引起的。另一方面,自动释放池只是对象的集合,这些对象的释放被推迟到池耗尽为止。池不控制是否释放某些东西,而只是控制这种释放的时间。重新映射视图,您无法控制它的缓存功能(使用内存,但不是真正的泄漏),如果存在真正的泄漏也不做任何事情(而且我不知道有任何重大的地图视图泄漏,尽管历史上有UIKit 中随机、适度的泄漏)。
  • @matt 是的,我看到了类似的行为。所以我用NSImage/UIImage 对象重复了我的练习,并更一致地表现出问题(坦率地说,这是一个更常见的问题示例,因为峰值内存使用通常只在处理较大的对象时才会出现问题;a这方面的实际示例可能是调整一堆图像大小的例程)。我还重现了调用明确创建自动释放对象的 Objective-C 代码的行为。不要误会我的意思:我认为我们在 Swift 中需要自动释放池的频率低于在 Objective-C 中,但它仍然可以发挥作用。
  • 我找到了一个有效的例子!只需反复调用 NSBundle 的pathForResource:ofType:
  • 我的 pathForResource:ofType: 示例不再适用于 Xcode 6.3 / Swift 1.2。 :)
【解决方案2】:

如果您将在等效的 Objective-C 代码中使用它,那么您将在 Swift 中使用它。

Swift 会足够聪明,知道 number 变量应该是 在第一个}

之后发布

仅当 Objective-C 可以。两者都按照 Cocoa 内存管理规则运行。

当然,ARC 知道 number 在循环迭代结束时超出范围,如果它保留它,它将在那里释放它。但是,这并不能告诉您对象是否被自动释放,因为-[NSNumber numberWithInt:] 可能会也可能不会返回一个自动释放的实例。没有办法知道,因为你无权访问-[NSNumber numberWithInt:]的来源。

【讨论】:

  • 如果 Swift 在这方面表现与 Objective-C 相同,为什么演示文稿提到“使用 Objective-C?”具体来说?
  • @Ethan 看起来原生 Swift 对象不是自动释放对象,autoreleasepool 构造是完全没有必要的。但是,如果您的 Swift 代码正在处理 Objective-C 对象(包括 Cocoa 对象),那么它们确实遵循自动释放模式,因此 autoreleasepool 构造变得有用。
  • 我知道“自动释放池允许您在 Swift 中释放自动释放对象时显式管理”,但我为什么要这样做?为什么编译器不能/不能为我做这件事?我必须添加我自己的自动释放池,以防止 VM 在巨大的字符串操作的紧密循环中通过屋顶。对我来说很明显应该在哪里添加它,而且效果很好。为什么编译器做不到呢?编译器能否变得更聪明以做好它?
【解决方案3】:

@autoreleasepool 可用于 Objective-C 和 Swift 代码,以保证使用依赖于 autorelease 的 Objective-C 代码

[Under the hood]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-26
    相关资源
    最近更新 更多