在我提交雷达之前,只需与社区进行健全检查:
在.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
其他信息:
==
。字典键类型的哈希值类型必须是可清除的才能用作字典的键类型 - 也就是说,类型必须提供一种计算自身哈希值的方法。哈希值是一个Int值,对于比较相等的所有对象都是相同的,这样如果a == b,则跟随a.hashValue == b.hashValue。
默认情况下,所有Swift的基本类型(如String,Int,Double和Bool)都是可以清除的,并且所有这些类型都可以用作字典的键。没有关联值的枚举成员值(如枚举中所述)在默认情况下也是可以清除的。
请注意 您可以使用自己的自定义类型作为字典键类型,使其符合Swift标准库中的Hashable协议。符合Hashable协议的类型必须提供名为hashValue的gettable Int属性,并且还必须提供“is equal”运算符(==)的实现。类型的hashValue属性返回的值在同一程序的不同执行或不同程序中不需要相同。 有关符合协议的更多信息,请参阅协议。
答案 0 :(得分:9)
NSObjectProtocol
不会从Hashable
继承。这是至关重要的问题。
它实际上无法继承Hashable
因为Hashable
需要名为hashValue
的方法,而NSObjectProtocol
需要名为hash
的方法。
另一方面,NSObject
类可以实现NSObjectProtocol
和Hashable
。
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>