【问题标题】:Converting non-escaping value to 'Any' may allow it to escape error - within not escaping function将非转义值转换为“任何”可能允许它转义错误 - 在非转义函数内
【发布时间】:2021-02-03 20:52:10
【问题描述】:

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

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关键字就可以了,但我想知道为什么编译器在这种情况下需要关键字。

【问题讨论】:

    标签: swift escaping closures


    【解决方案1】:

    print 接受(可变数量的)Any 作为参数,这就是为什么当您将闭包传递给print 时,它会将闭包转换为Any

    对闭包类型的参数进行了许多检查,以确保非转义闭包不会逃逸(关于“逃逸”闭包的含义,请阅读this):

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

    但是,这些检查仅适用于闭包类型。如果将闭包强制转换为Any,则闭包会丢失其闭包类型,并且编译器无法检查它是否转义。假设编译器允许您将非转义闭包强制转换为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($0)
        }
        operation()
    }
    

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

    【讨论】:

    • 您应该提到developer.apple.com/documentation/swift/…,在print 的情况下使用它是安全的(实际上,它实际上并没有逃脱关闭)
    • @Sweeper 感谢您快速而清晰的回答。我对此有几乎相同的假设,但我没有足够的证据。你的回答让我的想法很清楚。再次感谢。我想等待任何其他答案,我会总结这些答案并将其发布到日本委员会。
    • @AlexZavatone 感谢您提供更多信息。我知道这一点,但我不确定我应该在哪种情况下使用这种方法。你的评论说得很清楚。刚才我尝试了我的代码来检查如何使用 withoutActuallyEscaping(_:do:)。谢谢!
    猜你喜欢
    • 2020-02-04
    • 2019-01-05
    • 2019-02-19
    • 2016-12-23
    • 2017-07-02
    • 1970-01-01
    • 1970-01-01
    • 2021-12-23
    • 2012-02-10
    相关资源
    最近更新 更多