在泛型类型数据中使用闭包时是否转义



在下面的示例中,测试闭包作为函数参数传入,不需要@sescape。这是否意味着它被视为noescape关闭?我想知道这是否是一种解决方案,以避免由于转义而导致的堆分配。

func test() {
print("hello")
}
class b<T> {
let closure: T
// does not requires init(c: @escaping () -> Void)
init(c: T) {
self.closure = c
}
}
var c = b(c: test)

这是否意味着它算作noescape闭包?

不,当然不是。它转义传递给它的函数的生存期(在本例中为init(c:)),因此根据定义,它是转义的。您不必将其标记为@escaping,因为只有闭包函数参数(即类型为函数本身的函数参数)在默认情况下是不转义的(根据SE-0103)。

因此,因为闭包是转义的,所以它需要捕获的任何状态都进行堆分配。这种状态不可能被堆栈分配,因为它的生存期不限于当前堆栈帧。

然而,在您的情况下,您只是传递了一个简单的全局函数test。这里不需要进行额外的堆分配,因为全局函数是静态存储的,因此只需要传递一个简单的指针。

尽管值得注意的是,因为您使用的是泛型,为了将函数键入为给定的泛型占位符T,Swift将使用reabstration thunk来统一调用约定。要做到这一点,将使用堆分配框来存储函数值(我将在本问答中对此进行更详细的介绍)。

然而,在优化的构建中,thunk在某些情况下似乎能够专门化(例如直接使用顶级函数),因此意味着不需要进行额外的堆分配。

这是否意味着它算作noescape闭包?

您的通用版本欺骗了编译器。

编译器似乎会查找任何类型为闭包并在方法中使用的方法参数。不检查它是如何使用的,例如,如果它实际上被称为:

class Test {
var closure: Any
init(c: ()->Void) {
self.closure = c //Error: Non-Ecaping parameter 'c' may only be called
}
}

通过将闭包设置为泛型类型(T),它忽略了T可能是闭包的事实。如果它不忽视这一点,它就必须抱怨每一个通用的论点,因为这可能是一个结束。

我想知道这是否是一种解决方案,可以避免由于转义而导致的堆分配。

基于意见:编译器的警告和错误是有原因的。我恳求不要抑制它们,尤其是出于过早优化的原因,比如"解决问题以避免堆分配"。

最新更新