Protocol with associated type
问题
为什么协议可以当作类型使用,而带有关联类型的协议 (Protocol with associated type) 不能?
/// 编译通过
let foo: ContainerInt
/// 编译失败:Protocol 'Container' can only be used as a generic constraint because it has Self or associated type requirements
let bar: Container
protocol ContainerInt {
mutating func append(_ item: Int)
}
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
}
解答
声明变量将类型设为一个 Protocol 时,因为不知道具体类型,所以编译器会创建一个叫做 Existential Container 的中间类型。 Existential Container 会记录实际类型的 properties ,还有一个指向 Protocol Witness Table 的指针,可以找到该类型实现 Protocol 方法的实际位置。
普通 Struct 和 Existential Container 的内存检视
let foo = Stack()
let bar: ContainerInt = Stack()
print(MemoryLayout.size(ofValue: foo)) // 16 字节
print(MemoryLayout.size(ofValue: bar)) // 40 字节,Existential Container 40 字节
struct Stack: ContainerInt {
let x = 1
let y = 2
mutating func append(_ item: Int) {}
}
但是编译器不能为 PAT(Protocol with associated type)生成 Existential Container,所以不能把 PAT 当成类型使用。
比如两个变量符合同一个协议,但是关联类型(associated type)有可能不一样,有一些协议方法就会有问题
/// 如果允许 PAT,下面的写法会编译通过,然而 Int(15 的关联类型)和 String(“16” 的关联类型)是不能比较的。
(15 as Comparable) < ("16" as Comparable)
/// Comparable 的定义,Self 也算一种关联类型
public protocol Comparable : Equatable {
static func < (lhs: Self, rhs: Self) -> Bool
}
参考: