在下面的示例中,测试闭包作为函数参数传入,它不需要@escaping。这是否意味着它被视为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)
答案 0 :(得分:2)
这是否意味着它被视为noescape闭合?
不,当然不是。它会转移传递给它的函数的生命周期(在这种情况下为init(c:)
) - 因此按照定义,它正在转义。您只需将其标记为@escaping
,因为仅闭包函数参数(即作为函数本身键入的函数参数)默认情况下是非转义的(根据{{3 }})。
因此,因为闭包是转义的,所以它需要捕获的任何状态为堆分配。这种状态不可能是堆栈分配的,因为它的生命周期不限于当前的堆栈帧。
但是在你的情况下,你只是传递一个简单的全局函数test
。由于全局函数是静态存储的,因此不需要在此处进行额外的堆分配,因此只需要传递一个简单的指针。
虽然值得注意的是,因为您正在使用泛型,为了将函数键入为给定的通用占位符T
,Swift将使用reabstraction thunk以便统一调用约定。为此,将使用堆分配的框来存储函数值(我在SE-0103中更详细地介绍了这一点。)
但是,在优化构建中,在某些情况下this Q&A(例如直接使用顶级函数),因此意味着不需要额外的堆分配。
答案 1 :(得分:1)
这是否意味着它被视为noescape闭合?
您的通用版本会欺骗编译器。
编译器似乎在寻找任何类型为closure的方法参数,并在方法中使用。不检查它是如何使用的,例如,如果它实际上被称为:
class Test {
var closure: Any
init(c: ()->Void) {
self.closure = c //Error: Non-Ecaping parameter 'c' may only be called
}
}
通过将闭包设置为泛型类型(T
),它忽略了T
可能是闭包的事实。如果它不会忽略这一点,它就会对每个泛型参数抱怨,因为它可能是一个闭包。
我想知道这是否是一种解决方法,以避免因转义而导致堆分配。
基于意见:编译器警告和错误是有原因的。我反对压制它们,特别是因为过早的优化原因,例如&#34;努力避免堆分配&#34;。