7

在我提交雷达之前,只需与社区进行一次完整性检查:

在 .h Obj-C 文件中:

@protocol myProto <NSObject> 
@end

在 .swift 文件中(可以通过桥接头访问上述协议定义):

class myClass {
    // This line compiles fine
    var dictOne: [NSObject:Int]?
    // This line fails with "Type 'myProto' does not conform to protocol 'Hashable'"
    var dictTwo: [myProto:Int]?
}

对 NSObject 类的检查表明它(或它映射到的 NSObjectProtocol)没有实现 Hashable 协议所需的 hashValue 方法,也没有明确采用它。

因此,尽管如此,在幕后某处 NSObject 仍被标记为 Hashable,但并未扩展到采用 NSObject/NSObjectProtocol 的协议。

我有错误还是我错过了什么?

:) 张

附加信息:

文档表明:

  • 字典的键类型的唯一要求是它是 Hashable 并且它实现了==.
  • 您确实可以使用协议。
字典键类型的哈希值

类型必须是可散列的,才能用作字典的键类型——也就是说,该类型必须提供一种为其自身计算散列值的方法。哈希值是对于所有比较相等的对象都相同的 Int 值,因此如果 a == b,则遵循 a.hashValue == b.hashValue。

Swift 的所有基本类型(例如 String、Int、Double 和 Bool)默认都是可散列的,所有这些类型都可以用作字典的键。默认情况下,没有关联值的枚举成员值(如枚举中所述)也是可散列的。

注意你可以使用你自己的自定义类型作为字典键类型,方法是让它们符合 Swift 标准库中的 Hashable 协议。符合 Hashable 协议的类型必须提供称为 hashValue 的可获取 Int 属性,并且还必须提供“等于”运算符 (==) 的实现。类型的 hashValue 属性返回的值不需要在同一程序的不同执行或不同程序中相同。有关遵守协议的更多信息,请参阅协议。

4

3 回答 3

11

NSObjectProtocol不继承自Hashable. 这就是这里的关键问题。

它实际上不能继承自,Hashable因为Hashable需要调用的方法hashValue,而NSObjectProtocol需要调用的方法hash

另一方面,NSObject类可以同时实现NSObjectProtocolHashable

同样的问题发生在Equatable.

编辑:

还有一个更微妙的问题。您不能在Equatable预期的地方使用协议,您总是需要使用类类型或采用的值类型Equatable。原因是一个键采用是不够的Equatable,字典中的所有键必须彼此相等。

例如,如果你有一个 classA和一个 class B,两者都符合Equatable,那么你可以比较 of 的实例和 ofA的其他实例,A你可以比较 of 的实例和 ofB的其他实例,B但是你不能比较 ofA的实例和 的实例B。这就是为什么您不能在同一个字典中使用实例A和实例作为键的原因。B

请注意, everyNSObject与任何 other 是相等的NSObject,因此NSObject字典中键的允许类型也是如此。

于 2014-07-24T16:57:05.253 回答
0

我同意这似乎是一个缺失的功能。对于任何感兴趣的人,我做了一个小包装来处理它。

struct HashableNSObject<T: NSObjectProtocol>: Hashable {
    let value: T

    init(_ value: T) {
        self.value = value
    }

    static func == (lhs: HashableNSObject<T>, rhs: HashableNSObject<T>) -> Bool {
        return lhs.value.isEqual(rhs.value)
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(value.hash)
    }
}

T您甚至可以通过替换为非泛型NSObjectProtocol,但我认为这样更干净。

它相当容易使用,但是当您必须使用包含的值进行映射时会有点长。

let foo = [MyObjectProtocol]()
let bar = Set<HashableNSObject<MyObjectProtocol>>()
fun1(foo.map { HashableNSObject($0) })
fun2(bar.map { $0.value })
于 2019-04-19T12:29:20.883 回答
0

或者,您可以使用 NSObjectProtocol.hash 作为您的密钥。

var dictTwo: [Int:Int]?
dictTwo[myProtoInstance.hash] = 0
于 2020-07-02T03:07:32.360 回答