【问题标题】:Issue passing a closure that takes an escaping closure to a function that accepts a closure of that type将带有转义闭包的闭包传递给接受该类型闭包的函数的问题
【发布时间】:2017-01-31 07:32:41
【问题描述】:

在旧的 swift 世界(我相信是 2.0)中,我有以下 Y-combinator 实现

func Y<T, R>( f: (T -> R) -> (T -> R) ) -> (T -> R) {
    return { (t: T) -> R in
        return f(self.Y(f))(t)
    }
}

我会在别处调用 Y-comb 来创建递归闭包,如下所示:

let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
let repeatClosure = self.Y {
    (f: () -> () ) -> (() -> ()) in
    return {
        if self.responses_received != responses_expected {
            dispatch_after(delayTime, dispatch_get_main_queue()) {                         
                // Resend 
                NSNotificationCenter.defaultCenter().
                    postNotificationName(sendData, 
                    object: nil, userInfo: dataToSend)

                f()
            }
        } else {
            print("Completed!")
            self.responses_received = 0
        }
    }
}

repeatClosure()

这个想法是,当“sendData”通知的观察者收到他的响应时,他会向包含我的 Y 组合器和重复闭包的类发送通知。一旦 'sendData' 通知的所有观察者实例都收到了它们的数据,

self.responses_received == responses_expected

会是真的,我们不会再调用 f()。

现在,我的问题是转换为 Swift 3.0 迫使我在 Y 的定义上明确声明“f”的类型为 @escaping:

func Y<T, R>( _ f: @escaping ((T) -> R) -> ((T) -> R) ) -> ((T) -> R) {
    return { (t: T) -> R in
        return f(self.Y(f))(t)
    }
}

随后将我的重复闭包转换为具有相同类型。这很好,我了解@escaping 和@noescape 之间的区别以及我的代码为什么需要它。但是,在尝试构建时,我收到关于类型不匹配的编译错误:

Cannot convert value of type '(@escaping () -> ()) -> (() -> ())' 
to expected argument type '(() -> ()) -> (() -> ())'

我创建了一个简单的示例闭包,只是为了测试它会给出相同的错误:

let innerClosure = { (f: @escaping ()->()) -> (()->()) in
    return {}
}

let repeatClosure = self.Y(innerClosure)

而以下没有问题:

let innerClosure = { (f: ()->()) -> (()->()) in
    return {}
}

let repeatClosure = self.Y(innerClosure)

我得到了 fixit 的建议,强制强制转换为类型,没有 @escaping 标记。这可以编译,但感觉不对,我还没有真正测试过该演员表是否真的会在运行时工作。

我在这里错过了什么?

【问题讨论】:

    标签: ios swift closures y-combinator


    【解决方案1】:

    我在操场上有这段代码,它的构建没有任何问题:

    class Foo {
      func Y<T,R>(_ f: @escaping ((T) -> R) -> ((T) -> R) ) -> ((T) -> R) {
        return { (t: T) -> R in
          return f(self.Y(f))(t)
        }
      }
    
      func test() {
        let innerClosure = { (f: ()->()) -> (()->()) in
          print("test")
          return {}
        }
    
        let repeatClosure = self.Y(innerClosure)
        repeatClosure()
      }
    }
    
    Foo().test() // prints "test"
    

    您可能想要清理您的构建并重新尝试构建,因为我在操场上没有得到修复建议。

    【讨论】:

      【解决方案2】:

      您的Y 函数应具有以下签名:

      func Y<T, R>(_ f: @escaping (@escaping (T) -> R) -> ((T) -> R)) -> ((T) -> R)
      

      因为它接受本身需要转义函数的转义函数。

      当您调用Y 时,闭包应以:

      self.Y { (f: @escaping () -> ()) -> (() -> ()) in
      

      请注意,这个@escaping 对应于Y 签名中转义的。这是导致您的编译器错误的 @escaping 的不匹配。

      其余的大部分应该自行解决(一旦您将 Dispatch 和 NSNotificationCenter 调用更新为 Swift 3)。

      【讨论】:

        猜你喜欢
        • 2021-01-07
        • 1970-01-01
        • 2018-06-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-22
        相关资源
        最近更新 更多