func perform(_ clsr: () -> Void) {
let anotherClosure = {
clsr()
}
}
上面的代码被编译,但是当将
anotherClosure
更改为 var
时,编译器给出错误:
转义闭包捕获非转义参数'clsr'
func perform(_ clsr: () -> Void) {
var anotherClosure = { // error: Escaping closure captures non-escaping parameter 'clsr'
clsr()
}
}
还有
func perform(_ clsr: () -> Void) {
// now it's let still I get the error
let anotherClosure = {
clsr()
}
anotherClosure() // error: Escaping closure captures non-escaping parameter 'clsr'
}
但是
func perform(_ clsr: () -> Void) {
let anotherClosure = {
clsr()
}
anotherPerfom(anotherClosure) // Now it's works fine
}
func anotherPerfom(_ clsr: () -> Void){
clsr()
}
幕后发生了什么?谁能给我解释一下吗?
须知:
转义意味着函数被“存储”在某个地方以供以后执行。如果要存储函数参数is以供以后执行,则其函数类型必须标记为@escaping
。
好的,我们开始吧。
func perform(_ clsr: () -> Void) {
let anotherClosure = {
clsr()
}
}
这会初始化一个不可变值,
anotherClosure
,而无需任何后续引用。因此,从编译器的角度来看,这几乎就像
perform
没有内容一样。您会收到一条警告,指出您有一个未引用的
let
,但仅此而已;编译器不需要考虑 anotherClosure
的 content,因为
anotherClosure
将被优化掉。
func perform(_ clsr: () -> Void) {
var anotherClosure = {
clsr()
}
}
和以前一样,但现在let
是
var
。这意味着
anotherClosure
不会被优化掉;这次是“真实的”。因此 anotherClosure
是通过 storing在其中初始化一个闭包,该闭包使用其闭包
capturing功能来引用(从而捕获)
clsr
参数。
因此,您所做的正是错误消息所说的:anotherClosure
正在使用转义闭包(大括号)进行初始化,该闭包捕获clsr
,这是一个非转义参数(因为您没有说
@escaping
) 。要解决这个问题,你会说func perform(_ clsr: @escaping () -> Void) {
func perform(_ clsr: () -> Void) {
let anotherClosure = {
clsr()
}
anotherClosure()
}
这就像第一个,带有 let
,但现在is
是对
anotherClosure
的后续引用,因此 anotherClosure
无法被优化掉。但这意味着情况与第二个(带有var
的那个)完全相同;编译器必须考虑anotherClosure
如何存储一个闭包(花括号),它
捕获传入参数
clsr
,并且它得出与前面的示例完全相同的结论,完全相同原因。
最后但并非最不重要的一点:
func perform(_ clsr: () -> Void) {
let anotherClosure = {
clsr()
}
anotherPerform(anotherClosure)
}
func anotherPerform(_ clsr: () -> Void) {
clsr()
}
这个例子就像第一个例子一样——这是一个可以优化的
let
。如果你说
var
,我们就会回到第二个例子,但你没有。和你说的一模一样
func perform(_ clsr: () -> Void) {
anotherPerform({ clsr() })
}
func anotherPerform(_ clsr: () -> Void) {
clsr()
}
这样写,我们可以看到,事实上,任何一个 clsr
值都没有发生存储;第一个
clsr
函数会立即执行。因此,没有任何内容被转义,并且函数类型不必声明为 @escaping
。