NSObject是Hashable但是采用NSObject的协议不是吗?

时间:2014-07-24 15:54:20

标签: objective-c swift protocols hashable

在我提交雷达之前,只需与社区进行健全检查:

在.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的协议。

我有错误或错过了什么吗?

:) TEO

其他信息:

documentation表明:

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

类型必须是可清除的才能用作字典的键类型 - 也就是说,类型必须提供一种计算自身哈希值的方法。哈希值是一个Int值,对于比较相等的所有对象都是相同的,这样如果a == b,则跟随a.hashValue == b.hashValue。

     

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

     
    

请注意     您可以使用自己的自定义类型作为字典键类型,使其符合Swift标准库中的Hashable协议。符合Hashable协议的类型必须提供名为hashValue的gettable Int属性,并且还必须提供“is equal”运算符(==)的实现。类型的hashValue属性返回的值在同一程序的不同执行或不同程序中不需要相同。     有关符合协议的更多信息,请参阅协议。

  

3 个答案:

答案 0 :(得分:9)

NSObjectProtocol不会从Hashable继承。这是至关重要的问题。

它实际上无法继承Hashable因为Hashable需要名为hashValue的方法,而NSObjectProtocol需要名为hash的方法。

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

Equatable也会出现同样的问题。

修改

还有另一个更微妙的问题。您无法在需要Equatable的地方使用协议,您始终需要使用类型或采用Equatable的值类型。原因是密钥不足以采用Equatable,字典中的所有密钥必须彼此相等。

例如,如果您有一个类A和一个类B,两者都符合Equatable,那么您可以将A的实例与A的其他实例进行比较{1}}您可以将B的实例与B的其他实例进行比较,但无法将A的实例与B的实例进行比较。这就是为什么您不能将A的实例和B的实例用作同一字典中的键的原因。

请注意,每个NSObject都与其他NSObject等同,因此NSObject是字典中键的允许类型。

答案 1 :(得分: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 })

答案 2 :(得分:0)

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

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.main.PlaceholderFragment">

    <TextView
        android:id="@+id/section_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/activity_horizontal_margin"
        android:layout_marginTop="@dimen/activity_vertical_margin"
        android:layout_marginEnd="@dimen/activity_horizontal_margin"
        android:layout_marginBottom="@dimen/activity_vertical_margin"
        android:text="@string/sparta"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:layout_constraintLeft_creator="1"
        tools:layout_constraintTop_creator="1" />

</androidx.constraintlayout.widget.ConstraintLayout>