警告
此软件包需要 Swift 5.9.2,该版本随 Xcode 15.1 一起发布。此软件包将无法使用 Swift 5.9.0(Xcode 15.0 和 15.0.1)编译。
@Hashable
是一个 Swift 宏,用于添加 Hashable
一致性。当无法合成一致性时,它特别有用,例如对于类或具有一个或多个不可哈希属性的结构体。
@Hashable
宏应用于将符合 Hashable
的类型,而 Hashed
宏应用于每个应有助于 Hashable
一致性的属性。
import HashableMacro
/// A struct that uses the ``stringProperty`` and ``intProperty`` for `Hashable` conformance.
@Hashable
struct MyStruct {
// Any property that is hashable is supported.
@Hashed
let stringProperty: String
// Works on private properties, too.
@Hashed
private let intProperty: Int
// Non-decorated properties are ignored
let notHashableType: NotHashableType
}
所有装饰的属性都包含在 ==
和 hash(into:)
实现中,从而确保 Hashable
的约定得以维持。
两个相等的实例必须以相同的顺序将相同的值提供给
hash(into:)
中的Hasher
。
HashableMacro 的 DocC 文档由 Swift Package Index 托管。大部分实现可以在 Macros.swift 文件中看到。
@NotHashed
宏可以应用于*不应*包含在 Hashable
一致性中的属性。如果使用此宏来装饰属性,则不应使用 @Hashed
宏来装饰同一类型中的属性。
这对于具有较少数量的不可哈希属性而不是可哈希属性的类型很有用。
/// A struct that uses the ``stringProperty`` and ``intProperty`` for `Hashable` conformance.
@Hashable
struct MyStruct {
// Implicitly used for `Hashable` conformance
let stringProperty: String
// Implicitly used for `Hashable` conformance
private let intProperty: Int
// Explicitly excluded from `Hashable` conformance
@NotHashed
let notHashableType: NotHashableType
}
如果添加了 @Hashable
宏,但没有属性用 @Hashed
或 @NotHashed
装饰,那么将使用所有存储的属性。
/// A struct that uses the ``stringProperty`` and ``intProperty`` for `Hashable` conformance.
@Hashable
struct MyStruct {
// Implicitly used for `Hashable` conformance
let stringProperty: String
// Implicitly used for `Hashable` conformance
private let intProperty: Int
// Implicitly excluded from `Hashable` conformance
var computedProperty: Bool {
intProperty > 0
}
}
与不使用宏添加 Hashable
一致性相比,一个(相当小的)优点是您可以通过右键单击 → 展开宏来查看生成的代码。
当一个类型实现 NSObjectProtocol
(例如,它继承自 NSObject
)时,它应该覆盖 hash
和 isEqual(_:)
,而不是 hash(into:)
和 ==
。@Hashable
检测到它附加到符合 NSObjectProtocol
的类型时,将提供 hash
属性和 isEqual(_:)
函数。
@Hashable
还会提供一个 isEqual(to:)
函数,该函数接受一个与 Self
匹配的参数,该参数也将具有一个适当命名的 Objective-C 函数。
import HashableMacro
@Hashable
final class Person: NSObject {
@Hashed
var name: String = ""
}
extension Person {
override var hash: Int {
var hasher = Hasher()
hasher.combine(self.name)
return hasher.finalize()
}
}
extension Person {
override func isEqual(_ object: Any?) -> Bool {
guard let object = object as? Person else {
return false
}
guard type(of: self) == type(of: object) else {
return false
}
return self.isEqual(to: object)
}
@objc(isEqualToPerson:)
func isEqual(to object: Person) -> Bool {
return self.name == object.name
}
}
当将 @Hashable
宏添加到类时,生成的 hash(into:)
函数被标记为 final
。这是因为子类不应该重载 ==
。有很多原因表明这可能是一个坏主意,但特别是在 Swift 中,这不起作用,因为
!=
不是 Equatable
协议的一部分,而是 Equatable
的扩展,导致它总是使用添加 Equatable
一致性的类的 ==
实现!=
,但这仍然不是一个好主意,因为...XCTAssertEqual
,都将使用添加 Equatable
一致性的类的 ==
实现NSObject
,然后从 ==
调用该函数如果这对您的使用来说是一个问题,您可以将 finalHashInto: false
传递给宏,但它不会尝试调用 super
或使用超类的属性。
这不是该宏旨在解决的问题。