【问题标题】:Swift protocol with associated type and shadow type erasure具有关联类型和影子类型擦除的 Swift 协议
【发布时间】:2019-08-22 07:32:53
【问题描述】:

我需要将实现具有关联类型的协议的对象传递给接受协议的方法。这在 Swift(直到最新的 Swift 5)中是不可能的,所以我使用类型擦除和基于 this blog 的影子协议。

protocol ShadowA {
    func test(data: Any) -> String
}

extension ShadowA {
    func test(data: Any) -> String {
        return "shadow"
    }
}

protocol A: ShadowA {
    associatedtype AT

    func test(data: AT) -> String
}

class SpecificA: A {
    typealias AT = Int

    func test(data: Int) -> String {
        return "specific"
    }
}

问题是当我将对象传递给方法时,会调用“影子的默认实现”而不是“通用”的。您可以查看playground 以了解发生了什么。

有什么问题吗?或者这个用例在 Swift 中根本不可能?

【问题讨论】:

    标签: swift protocols swift-protocols associated-types


    【解决方案1】:

    这个动态调度和直接调度魔术的更多解释,检查这个swift-method-dispatch

    动态调度是选择哪个实现的过程 在运行时调用的多态操作(方法或函数)。

    protocol extension 中实现的任何方法都是直接调度

    我会解释更多

    因为ShadowAprotocol 并且在扩展名中有default implementation,所以编译器头脑有这个提示“Any class can adopt to this protocol without implement this method because it have default implementation

    直接派送

    所以在这一行编译器从 item 中知道 test(data:Any) 的内容,并且因为 item 是具有默认实现的协议,所以它指示它调用直接默认实现

    return "in method: \(item.test(data: 0))"
    
    func passedIntoMethod(item: ShadowA) -> String {
        return "in method: \(item.test(data: 0))"
    }
    

    也在这一行

    let a2: ShadowA = SpecificA()   // becuse compiler know only about a2 at this line that it is Protocol so it direct dispatch it  
    print(a2.test(data: 0))
    

    动态调度

    这里编译器知道 a1 是 concreate 类型,所以它调用在它内部实现的 test 方法。如果没有实现则调用默认实现

    let a1 = SpecificA()
    print(a1.test(data: 0))
    

    【讨论】:

    • 有趣的是,当我在一个接受影子协议的方法中有一个断点时,程序会暂停,然后当我从调试器调用该方法时,会调用正确的“通用”方法目的。然后跳过代码,它调用与我从调试器调用的方法相同的方法,调用“影子方法”,即调试器调用通用方法,但实际代码调用影子方法。
    • 调度在运行时
    • 所以调试器将方法内部的对象视为具体类型(而不仅仅是作为影子协议),因此它可以/将调用正确的方法?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多