警告
此软件包需要 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 或使用超类的属性。
这不是该宏旨在解决的问题。