Swift 编译器丢失了它的多态上下文

Swift Compiler lose its polymorphical context

首先,我是 Swift 语言的新手并且使用 python 工作,这是 'dynamic typed' 语言,所以不知道编译器的确切观点。

下面的代码是一个简单的代码片段,用于练习 Apple 所说的 POP(面向协议的编程)。 片段包含各种 ReturnClassesRunnerClasses(每个都有不同的 return 类型)和实际的 运行ner 只是 运行 RunnerClasses。还有 Selector 枚举假装动态调度。

protocol Return {
    var value: Int { get set }
    init(_ value: Int)
}

final class Return0: Return {
    var value: Int
    init(_ value: Int) {
        print("return 0")
        self.value = value
    }
}

final class Return1: Return {
    var value: Int
    init(_ value: Int) {
        print("return 1")
        self.value = value + 50
    }
}

final class Return2: Return {
    var value: Int
    init(_ value: Int) {
        print("return 2")
        self.value = value + 500
    }
}

protocol FirstProtocol {
    init()
    func a() -> Return
    func b() -> Return
}

extension FirstProtocol {
    func a() -> Return {
        print("protocol a hello")
        return self.b()
    }
    
    func b() -> Return {
        print("protocol b hello")
        return Return0(5)
    }
}

enum Selector {
    case first, second
    
    func cls() -> FirstProtocol{
        switch self {
        case .first:
            return First1Class()
        case .second:
            return First2Class()

        }
    }
}

final class First1Class: FirstProtocol {
    init() {
        
    }
    
    func b() -> Return1 {
        print("first 1 class hello")
        return Return1(3)
    }
}

final class First2Class: FirstProtocol {
    init() {
        
    }
    
    func b() -> Return2 {
        print("first 2 class hello")
        return Return2(5)
    }
}

final class Runner {
    var cls: FirstProtocol
    var selector: Selector
    
    init(_ selector: Selector) {
        self.cls = selector.cls()
        self.selector = selector
    }
    
    
    func createCls(cls: FirstProtocol.Type) -> FirstProtocol {
        return cls.init()
    }
    
    func run(cls: FirstProtocol.Type) {
        print("--- run 1 ---")
        (self.createCls(cls: cls) as! First2Class).a()
    }
    
    func run2(t: Selector) -> Return {
        print("--- run 2 ---")
        print(t.cls())
        return t.cls().b()
    }
    
    func run3() -> Return {
        print("--- run 3 ---")
        print(self.cls)
        return self.cls.a()
    }
}

final class Runner2 {
    var runner: Runner
    
    init(runner: Runner) {
        self.runner = runner
    }
    
    func run() -> Return {
        print("--- 2run 1 ---")
        return self.runner.run2(t: .second)
    }
}

var a = Runner2(runner: Runner(.second)).run()
print(type(of: a))
print(a.value)

低于结果 returns。

--- 2run 1 ---                           line 1
--- run 2 ---                            line 2
__lldb_expr_80.First2Class               line 3
protocol b hello                         line 4
return 0                                 line 5
Return0                                  line 6
5                                        line 7

这里我有一个问题。

line 3 中,这是 Runner.run2 的结果,我认为 __lldb_expr_80.First2Class 字符串意味着编译器确切地知道应该启动哪个 class。因此 line 4 应该是 first 2 class hello 而不是 protocol b hello。为什么打印protocol b hello以及如何打印first 2 class hello

这是因为 First2Class 中的 b 实际上并不是 FirstProtocolb 要求的见证(First1Class 也是如此) .也就是说,First2Class之所以符合FirstProtocol,并不是其中声明的b方法。协议要求的唯一见证只是协议扩展中的b

这是因为这些 b 的 return 类型与协议要求的 return 类型不匹配:

// protocol requirement
func b() -> Return {
    ...
}

// First1Class
func b() -> Return1 {
    ...
}

// First2Class
func b() -> Return2 {
    ...
}

Return 类型必须完全匹配才能实现协议。查看更多讨论

当您在协议类型上调用 b 时,例如此处:

return t.cls().b()

(t.cls() 编译时类型 FirstProtocol) 因为 b 不是 witness,你的 bs 在解决呼叫时不会被考虑。该调用只会解析到唯一的见证人,即协议扩展中的 b

...means compiler exactly knows which class should be initiated.

事实并非如此。请记住,您只能看到在 运行时 时打印的消息。编译器不知道 cls.

实例化了哪个 class

这是一个更短的例子来证明这一点:

class A {}
class B : A {}

protocol Foo {
    func foo() -> A
}

extension Foo {
    func foo() -> A {
        print("Foo")
        return A()
    }
}

class Bar : Foo {
    func foo() -> B {
        print("Bar")
        return B()
    }
}

let foo: Foo = Bar()
let bar = Bar()
foo.foo() // prints Foo
bar.foo() // prints Bar