Swift中Struct或Class遵循Hashable协议
Swift中Struct或Class遵循Hashable协议

Swift中Struct或Class遵循Hashable协议

Struct遵循Hashable协议

在Swift中,struct遵循 Hashable 和 Equatable 协议通常可以通过其所有属性自动生成所需的实现。但当结构体中的属性本身没有自动满足 Hashable 或 Equatable 的要求时,会导致错误。

struct Friend: Codable {
    var id: String
    var name: String
}

struct Friendface:Codable, Hashable {
    var id: String
    var isActive: Bool
    var name: String
    var age: Int
    var email: String
    var address: String
    var about: String
    var registered: String
    var tags: [String]
    var friends: [Friend]
}

上面的代码,Friendface遵循Hashable协议时,Xcode输出报错为:

Type 'Friendface' does not conform to protocol 'Equatable'
Add stubs for conformance
Type 'Friendface' does not conform to protocol 'Hashable'

Friendface 结构报错的原因是它的 friends 属性是一个 [Friend],而 Friend 可能没有遵循 Hashable 或 Equatable 协议。

解决方法

确保 Friend 也遵循 Hashable 和 Equatable 协议。

修改 Friend 结构

假设 Friend 的结构如下:

struct Friend: Codable, Hashable {
    var id: String
    var name: String
}

在这种情况下,Friend 中的所有属性都是 Hashable,因此编译器可以自动生成 Hashable 和 Equatable 实现。

Class遵循Hashable协议

如果将前面的Struct改为Class,并遵循Hashable协议:

class Friend: Codable {
    var id: String
    var name: String
}

class Friendface:Codable, Hashable {
    var id: String
    var isActive: Bool
    var name: String
    var age: Int
    var email: String
    var address: String
    var about: String
    var registered: String
    var tags: [String]
    var friends: [Friend]
}

因此,需要在Friendface类中实现Equatable和Hashable协议:

// 实现 Equatable 协议
static func == (lhs: Friendface, rhs: Friendface) -> Bool {
    return lhs.id == rhs.id
}

// 实现 Hashable 协议
func hash(into hasher: inout Hasher) {
    hasher.combine(id)
}
1、Equatable 协议

ForEach 需要比较对象的唯一性,Equatable 通过 == 方法提供对象比较的规则。

在这里,我们通过比较 id 属性来实现对象的等价性。

2、Hashable 协议

ForEach 使用 Hashable 来生成唯一的标识符。

实现 hash(into:) 方法时,使用 id 属性生成唯一的哈希值。

代码意义

1、Equatable
static func == (lhs: Friendface, rhs: Friendface) -> Bool {
    return lhs.id == rhs.id
}

作用:定义两个 Friendface 对象什么时候被认为是“相等”的。

这里的逻辑:当两个对象的 id 值相同时,认为它们是相等的。

解释:当 id 是唯一标识符时,这种实现很合理。

2、Hashable
func hash(into hasher: inout Hasher) {
    hasher.combine(id)
}

作用:生成对象的哈希值。

这里的逻辑:使用对象的 id 作为唯一标识符来生成哈希值。

如果没有 id 字段,应该怎么办?

1、寻找其他唯一标识

如果对象有其他属性可以唯一标识它,比如 name 或 email,可以改用这些字段。

示例:

static func == (lhs: Friendface, rhs: Friendface) -> Bool {
    return lhs.email == rhs.email
}

func hash(into hasher: inout Hasher) {
    hasher.combine(email)
}
2、使用多个字段联合比较

如果没有单一的标识符,可以通过多个属性联合定义相等性和哈希值。

示例:

static func == (lhs: Friendface, rhs: Friendface) -> Bool {
    return lhs.name == rhs.name && lhs.registered == rhs.registered
}

func hash(into hasher: inout Hasher) {
    hasher.combine(name)
    hasher.combine(registered)
}
3、完全比较所有属性

如果所有字段都很重要,可以比较所有属性来判断相等性,并将所有属性纳入哈希值计算。

示例:

static func == (lhs: Friendface, rhs: Friendface) -> Bool {
    return lhs.isActive == rhs.isActive &&
           lhs.name == rhs.name &&
           lhs.age == rhs.age &&
           lhs.email == rhs.email &&
           lhs.address == rhs.address &&
           lhs.about == rhs.about &&
           lhs.registered == rhs.registered &&
           lhs.tags == rhs.tags &&
           lhs.friends == rhs.friends
}

func hash(into hasher: inout Hasher) {
    hasher.combine(isActive)
    hasher.combine(name)
    hasher.combine(age)
    hasher.combine(email)
    hasher.combine(address)
    hasher.combine(about)
    hasher.combine(registered)
    hasher.combine(tags)
    hasher.combine(friends)
}

完整的Class代码

class Friend: Codable {
    var id: String
    var name: String
    
    init(id: String, name: String) {
        self.id = id
        self.name = name
    }
}

class Friendface:Codable, Hashable {
    var id: String
    var isActive: Bool
    var name: String
    var age: Int
    var email: String
    var address: String
    var about: String
    var registered: String
    var tags: [String]
    var friends: [Friend]
    
    init(id: String, isActive: Bool, name: String, age: Int, email: String, address: String, about: String, registered: String, tags: [String], friends: [Friend]) {
        self.id = id
        self.isActive = isActive
        self.name = name
        self.age = age
        self.email = email
        self.address = address
        self.about = about
        self.registered = registered
        self.tags = tags
        self.friends = friends
    }
    
    // 实现 Equatable 协议
    static func == (lhs: Friendface, rhs: Friendface) -> Bool {
        return lhs.id == rhs.id
    }
    
    // 实现 Hashable 协议
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}

因为前面的代码没有提供初始化方法,所以在完整的Class代码中补充了初始化方法。

完整的Class代码中,Friendface类实现了Equatable和Hashable协议,但是Friend类并没有实现这两个协议。

如果 Friend 只是作为 Friendface 的一个普通属性,并且不会单独比较或使用哈希值,则无需实现 Hashable 和 Equatable。

如果 Friend 的实例将用于比较或者存储在需要哈希值的容器(如 Set 或作为 Dictionary 的键)中,那么 Friend 必须实现 Hashable 和 Equatable。

因此,当前情况下,Friend 是 Friendface 的子属性。如果 Friendface 仅使用 id 属性来实现 Hashable 和 Equatable,那么 Friend 不需要额外实现这些协议。

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注