异步自动释放池

5

我遇到这样一个情况:在使用 async/await 循环创建许多 Cocoa 对象时,由于对象仅在循环结束时释放(而不是每次迭代),因此内存峰值会快速上升。

解决方法是使用 autoreleasepool。但是,我似乎无法将 autoreleasepoolasync/await 结合使用。

下面是一个示例:

func getImage() async -> NSImage? {
    return NSImage(named: "imagename") // Do some work
}

Task {
    // This leaks
    for _ in 0 ..< 1000000 {
        let image = await getImage()
        print(image!.backgroundColor)
    }
}

内存峰值一直飙升到220MB,对于我来说有点太多了。

通常,您可以将内部循环包装在autoreleasepool中,并解决问题,但是当我尝试使用async函数时,会出现以下错误:

Cannot pass function of type '() async -> ()' to parameter expecting synchronous function type

有没有什么解决方法?或者是否有另一种方法来实现在循环内释放Cocoa对象的同样目标?


然而,这并不是一个“泄漏”。内存在循环期间被使用,但在所有操作完成后会被释放,对吗? - matt
1
@matt 是的,你说得对。我的错。然而,在循环内存峰值很高。 - recaptcha
调度队列和 NSThread 管理它们自己的自动释放池。那么,"Task" 也可能是这样做的吗?如果是的话,只需将您的内部异步函数封装到一个 "Task" 中即可。虽然我没有亲自检查过,但了解这一点非常有趣! ;) - CouchDeveloper
1
你应该在Swift论坛上发布关于这个问题的帖子。https://forums.swift.org/ 我还没有看到有关自动释放池和actors如何交互的讨论。这可能是一个被忽视的细节。值得提出来! - Alexander
1
我无法重现这个问题。在我的async方法中,autoreleasepool编译并正常工作。 - matt
@matt,问题在于尝试在autoreleasepool块内调用异步方法。 - Germán
1个回答

8

@CouchDeveloper 提到将 getImage 放在一个 Task 中,因为它有自己的autoreleasepool,看起来很有效!

只需更改

func getImage() async -> NSImage? {
    return NSImage(named: "imagename") // Do some work
}

to

func getImage() async -> NSImage? {
    await Task {
        return NSImage(named: "imagename") // Do some work
    }.value
}

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