Swift 编译器丢失了它的多态上下文
Swift Compiler lose its polymorphical context
首先,我是 Swift 语言的新手并且使用 python 工作,这是 'dynamic typed' 语言,所以不知道编译器的确切观点。
下面的代码是一个简单的代码片段,用于练习 Apple 所说的 POP(面向协议的编程)。
片段包含各种 ReturnClasses、RunnerClasses(每个都有不同的 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
实际上并不是 FirstProtocol
中 b
要求的见证(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,你的 b
s 在解决呼叫时不会被考虑。该调用只会解析到唯一的见证人,即协议扩展中的 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
首先,我是 Swift 语言的新手并且使用 python 工作,这是 'dynamic typed' 语言,所以不知道编译器的确切观点。
下面的代码是一个简单的代码片段,用于练习 Apple 所说的 POP(面向协议的编程)。 片段包含各种 ReturnClasses、RunnerClasses(每个都有不同的 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
实际上并不是 FirstProtocol
中 b
要求的见证(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,你的 b
s 在解决呼叫时不会被考虑。该调用只会解析到唯一的见证人,即协议扩展中的 b
。
...means compiler exactly knows which class should be initiated.
事实并非如此。请记住,您只能看到在 运行时 时打印的消息。编译器不知道 cls
.
这是一个更短的例子来证明这一点:
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