HashableMacro

Tests Platforms

警告

此软件包需要 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

@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

如果添加了 @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 一致性相比,一个(相当小的)优点是您可以通过右键单击 → 展开宏来查看生成的代码。

NSObject 支持

当一个类型实现 NSObjectProtocol(例如,它继承自 NSObject)时,它应该覆盖 hashisEqual(_:),而不是 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
    }
}

final hash(into:) 函数

当将 @Hashable 宏添加到类时,生成的 hash(into:) 函数被标记为 final。这是因为子类不应该重载 ==。有很多原因表明这可能是一个坏主意,但特别是在 Swift 中,这不起作用,因为

如果这对您的使用来说是一个问题,您可以将 finalHashInto: false 传递给宏,但它不会尝试调用 super 或使用超类的属性。

这不是该宏旨在解决的问题。

许可

MIT