// MARK: - Third Party Library
public protocol TestProtocol: ExpressibleByStringLiteral {
init(str: String)
}
extension TestProtocol {
public init(stringLiteral value: String) {
self.init(str: value)
}
}
public struct Test {
public let str: String
public init(str: String) {
self.str = str
}
}
extension Test: TestProtocol {}
// MARK: - My Swift Package
extension Test {
public init(stringLiteral value: String) {
self.init(str: "Roman")
}
}
let test: Test = "Test"
print(test.str)
这是游乐场友好的代码片段,它按预期工作。我正在重写初始化程序,以便无论我们真正分配什么,代码总是打印“Roman”。
但是,一旦我将“第三方库”代码移至游乐场的“源”文件夹(以模仿第三方库的使用),行为就会发生变化:我重写的方法被忽略😣
这是不可能的。两个模块分别编译,当
extension Test: TestProtocol {}
编译后,编译器需要生成
Test
的协议见证表,以保证TestProtocol
的一致性。这个表主要存储了Test
中的哪个东西实现了TestProtocol
中的哪个要求。不知道 TestProtocol
类型的具体类型的调用者将使用此表来查找他们应该调用的实际实现,例如
func f<T: TestProtocol>(_ string: TestProtocol.StringLiteralType) -> T {
// compiler would generate code here that looks up the protocol witness table of T
// and finds the correct implementation of init to call
T.init(stringLiteral: string)
}
虽然编译器可以将
let test: Test = "Test"
转换为 let test: Test = Test(stringLiteral: "Test")
(这会在 init
扩展中调用 Test
),但它被实现为调用 ExpressibleByStringLiteral.init(stringLiteral:)
的协议见证。
生成协议见证表时,编译器显然不会看到你在
extension Test
中声明的那个,因为文件是单独编译的。
当然,如果编译器看到
extension Test
中声明的那个,它就会选择那个而不是默认的实现。
因此图书馆之外的任何事物都无法改变
let test: Test = "Test"
的作用。