在Swift中隐藏Hashable

时间:2016-03-12 02:53:50

标签: swift generics

如何隐藏实现[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会为我们创造这样的风暴。这毕竟是编译器的一部分。

这是唯一的方法吗?我错过了什么吗?

1 个答案:

答案 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需要能够将对象相互比较以解决两个事物具有相同哈希值的冲突。如何将HashyStructHashyClass进行比较?它不知道调用哪个函数来比较这两个对象。它不知道是否在hashValueHashyStruct上拨打HashyClass

但是Objective-C做到了......

如果要在运行时动态执行此操作,实际上可以实现此目的。 &#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很棒的东西是你并没有与堆上分配的类和对象联系在一起。默认情况下,它可以为您提供良好的性能,并在您需要时选择动态行为。