如何隐藏实现[Hashable: Any]
的具体类型的Hashable
?
Swift有两种类型的协议:可以用作类型的协议和只能用作类型约束的协议。虽然这很强大,但也很不幸。这意味着某些类型的信息隐藏不是直接可能的。
我能想到的唯一解决方案是使用thunk:
struct Hash: Hashable {
private let value: Any
private let equals: Hash -> Bool
init<H: Hashable>(_ h: H) {
self.value = h
self.hashValue = h.hashValue
self.equals = { ($0.value as! H) == h }
}
let hashValue: Int
}
func ==(lhs: Hash, rhs: Hash) -> Bool {
return lhs.equals(rhs)
}
也许有一天Swift会为我们创造这样的风暴。这毕竟是编译器的一部分。
这是唯一的方法吗?我错过了什么吗?
答案 0 :(得分:1)
这背后的原因是像Hashable
这样的快速协议可以应用于所有不同的类型,而不仅仅是类。
假设我们有两种不同的类型:一个占用8位内存的结构和一个指向堆上一些内存块的指针。
struct HashyStruct : Equatable, Hashable {
let smallNumber: UInt16
var hashValue: Int {
return Int(smallNumber)
}
}
func ==(lhs: HashyStruct, rhs: HashyStruct) -> Bool {
return lhs.smallNumber == rhs.smallNumber
}
class HashyClass : Equatable, Hashable {
let number: UInt64
init(number: UInt64) {
self.number = number
}
var hashValue: Int {
return Int(number)
}
}
func ==(lhs: HashyClass, rhs: HashyClass) -> Bool {
return lhs.number == rhs.number
}
这两种类型都是Hashable
,为什么我们不能拥有这样的字典:
let anyHashable: [Hashable:Any] = [HashyStruct(smallNumber: 5) : "struct", HashyClass(number: 0x12345678):"class"]
错误:使用&#39; Hashable&#39;作为符合协议的具体类型&#39; Hashable&#39;不受支持 错误:协议&#39; Hashable&#39;只能用作通用约束,因为它具有Self或关联类型要求
Dictionary
需要能够将对象相互比较以解决两个事物具有相同哈希值的冲突。如何将HashyStruct
与HashyClass
进行比较?它不知道调用哪个函数来比较这两个对象。它不知道是否在hashValue
或HashyStruct
上拨打HashyClass
。
如果要在运行时动态执行此操作,实际上可以实现此目的。 &#34; thunk&#34;你实现的确有这种动态行为,但它会带来性能损失,在大多数情况下你不需要采取。
我假设您知道如何将哈希表实现为固定大小的数组,并将哈希值映射到可能存在冲突的有限数量的存储桶。
Dictionary
分配一块内存来存储它的对象;让它给4个64字节的部分(64位机器上指针的大小)。
| 64 bytes | 64 bytes | 64 bytes | 64 bytes |
当您添加两个具有哈希值1和6 = 2%4的项目时,您将获得以下哈希表:
| empty | item 1 | item 2 | empty |
这一切都很好;我们可以将任意数量的指针对象放入我们的表中,这将适用于类。但是在快速我们有结构 - HashyStruct
只有16位。如果我们给存储HashyStruct
的字典提供相同数量的内存,那么我们可以在哈希集中存储16个而不是4个项目。
| 16 bytes | 16 bytes | 16 bytes | 16 bytes | 16 bytes | 16 bytes | 16 bytes | 16 bytes | 16 bytes | 16 bytes | 16 bytes | 16 bytes | 16 bytes | 16 bytes | 16 bytes | 16 bytes |
只要编译器知道类型的大小,我们就可以在哈希表中拥有任何我们想要的类型。但是当我们有两种不同的类型...... ???
| 4 structs | item 1 | item 2 | empty |
你得到一个没有意义的哈希表。编译器不知道数组中项目的大小,因此它不能对它们编制索引。
很多让swift很棒的东西是你并没有与堆上分配的类和对象联系在一起。默认情况下,它可以为您提供良好的性能,并在您需要时选择动态行为。