Swift 闭包类型转换

标签 swift closures

我定义了一个协议(protocol)MessageProtocol,类MessageAMessageB采用了该协议(protocol)。

我想将 (MessageA) -> Void 之类的闭包转换为 (MessageProtocol) -> Void

有代码,解决方案12不能很好地工作。

protocol MessageProtocol {
    func doSomething()
    static func parserFromData() -> MessageProtocol
}

class MessageA: MessageProtocol {
    func doSomething() {
        print("this is A class")
    }

    func functionInA() {
        print("funciton in A")
    }

    static func parserFromData() -> MessageProtocol {
        return MessageA()
    }
}

class MessageB: MessageProtocol {
    func doSomething() {
        print("this is B class")
    }

    func functionInB() {
        print("funciton in b")
    }

    static func parserFromData() -> MessageProtocol {
        return MessageB()
    }
}

// private function .network callback
func callback(type: MessageProtocol.Type, handler: (MessageProtocol?) -> Void) -> Void {
    let obj = type.parserFromData()
    handler(obj)
}

// public function
func response<T: MessageProtocol>(type: T.Type, handler: (T?) -> Void) -> Void {

    // solution 1. compile error
//    let handler: (ABProtocol) -> Void = handler     // compile error
//    callback(type, handler: handler)

    // solution 2. runtime error
//    let handler = handler as! (ABProtocol) -> Void  // runtime error
//    callback(type, handler: handler)

    // solution 3. now i use
    callback(type) { obj in
        handler(obj as? T)
    }
}

response(MessageA.self) { a in
    guard let a = a else {
        return
    }
    a.doSomething()
    a.functionInA()
}

response(MessageB.self) { b in
    guard let b = b else {
        return
    }
    b.doSomething()
    b.functionInB()
}

最佳答案

给定一些协议(protocol)和实现该协议(protocol)的具体类,您将无法将闭包的参数从类向上转换为协议(protocol)。

需要明确的是,考虑到协议(protocol)和类的层次结构:

protocol FooProtocol {}

class FirstClass: FooProtocol {}
class SecondClass: FooProtocol {}

我们永远无法实现这个闭包:

var firstClosure: (FirstClass) -> Void

截止此关闭:

var fooClosure = firstClosure as (FirstClass) -> Void

尽管事实上编译器会非常乐意让我们转换类本身的变量。例如:

let first: FirstClass = FirstClass()
let foo = first as FooProtocol

它甚至不需要是 as?as! 。编译器知道FirstClass实现协议(protocol),这将永远通过。

闭包的问题在于我们不只是转换参数——我们正在尝试转换整个闭包。

此示例应确定编译器为何会提示转换:

protocol DoThingProtocol {
    func doThing()
}

class FooClass: DoThingProtocol {
    var shouldDoThing = false
    func doThing() {
        print("Foo did the thing")
    }
}

class BarClass: DoThingProtocol {
    func doThing() {
        print("Bar did the thing")
    }
}

现在,假设我们创建一个采用 FooClass 的闭包:

let fooClosure: (FooClass) -> Void = { thing in 
    if thing.shouldDoThing {
        thing.doThing()
    }
}

闭包被定义为采用 FooClass这个具体的闭包将参数视为 FooClass实例——它访问shouldDoThing FooClass的属性(property)BarClass 上未定义,尽管事实上 BarClass还实现了DoThingProtocol .

因为闭包会将传入的参数视为 FooClass并可能访问仅在 FooClass 中实现的参数部分而不是 FooClass 的任何东西继承自,只能传递FooClass及其后代关闭。您不能传递任何更上游的东西,或者 sibling 。

需要明确的是,这不仅限于协议(protocol)。如果DoThingProtocol哪里 DoThingClass和一个类实例 FooClass & BarClass继承自,我们会发现自己处于完全相同的情况。

如果您希望能够传递 FooClass 的两个实例和BarClass ,最好的选择是将闭包定义为采用 DoThingProtocol .

不管怎样,你可以贬低你的闭包。考虑这个非常简单的例子:

class A {}
class B: A {}

let closureA: (A) -> Void = { arg in
    print(arg)
}

let closureB = closureA as (B) -> Void

在本例中,B保证实现 A 的所有内容已实现,因为B继承自A 。不过,制作这个 Actor 阵容不一定有很多目的。 Actor 阵容仅限制 A 的其他子级从被传递到closureB ,但由于闭包已经实现为类型 (A) -> Void ,我们无法利用关于类的任何额外知识 B在闭包内。

关于Swift 闭包类型转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36518752/

相关文章:

javascript - 使用闭包

ios - 在 SwiftUI 中拖动分隔符

ios - Swift:如何将 pod 链接到我自己的框架

ios - ARKit:在屏幕上查找 SCNNode 的坐标

cocoa - Swift 和 inout 参数中的闭包捕获变量

javascript - 在 javascript 闭包中访问变量

java - 有没有类似 LINQ for Java 的东西?

ios - 通过单元格中的按钮通过 segue 传递数据……不工作……

ios - 拉动刷新不执行刷新功能?

javascript - 为什么这个闭包会返回函数呢?