将非转义值转换为 'Any' 可能允许它转义错误 - 在不转义函数内

Converting non-escaping value to 'Any' may allow it to escape error - within not escaping function

我的问题来源于下面的日文问题。 这不是我的问题,但我正在尝试回答以下问题,但找不到合适的答案。

https://teratail.com/questions/298998

上面的问题会像下面这样简单化。

func executetwice(operation:() -> Void) {
    print(operation)
    operation()
}

此编译器要求在operation:标签后添加@escaping关键字,如

func executetwice(operation: @escaping () -> Void) {
    print(operation)
    operation()
}

但实际上operation块似乎并没有从这个块中逃脱。

另一种方式,

func executetwice(operation:() -> Void) {
    let f = operation as Any
    operation()
}

编译器还需要添加 @escaping 关键字。它只是向上转换为 Any。 在其他情况下,只是转换为相同的类型,它似乎是错误的。

func executetwice(operation:() -> Void) {
    let f = operation as () -> Void //Converting non-escaping value to '() -> Void' may allow it to escape
    operation()
}

我不确定为什么我需要添加没有转义条件的 @escaping 关键字。

只需添加 @escaping 关键字即可,但我想知道为什么编译器在这种情况下需要关键字。

print 接受(可变数量的)Any 作为参数,所以这就是为什么当你将闭包传递给 print.

对 closure-typed 参数应用了许多检查以确保 non-escaping 闭包不会逃逸(对于闭包“逃逸”的含义,阅读 ) :

var c: (() -> Void)?
func f(operation:() -> Void) {
    c = operation // compiler can detect that operation escapes here, and produces an error
}

但是,这些检查仅适用于闭包类型。如果将闭包强制转换为 Any,则闭包会丢失其闭包类型,并且编译器无法检查它是否转义。假设编译器允许您将 non-escaping 闭包强制转换为 Any,并将其传递给下面的 g

var c: Any?
func g(operation: Any) {
    // the compiler doesn't know that "operation" is a closure! 
    // You have successfully made a non-escaping closure escape!
    c = operation
}

因此,编译器设计得比较保守,将“转换为 Any”视为“进行闭包转义”。

但是我们确定print不会逃脱闭包,所以我们可以使用withoutActuallyEscaping:

func executetwice(operation:() -> Void) {
    withoutActuallyEscaping(operation) { 
        print([=12=])
    }
    operation()
}

将闭包转换为它自己的类型也会使闭包转义。这是因为 operation as () -> Void 是一个“相当复杂”的表达式,它产生一个 () -> Void 类型的值。我所说的“相当复杂”是指它足够复杂,以至于在将其传递给 non-escaping 参数时,编译器不会费心检查您正在转换的内容是否真的是 non-escaping,因此它假设所有演员都在逃跑。