在 Swift 中将 JSONEncoder 用于 Equatable 方法

Posted

技术标签:

【中文标题】在 Swift 中将 JSONEncoder 用于 Equatable 方法【英文标题】:Using JSONEncoder for Equatable method in Swift 【发布时间】:2018-10-29 15:55:18 【问题描述】:

我知道 JSON 键没有任何顺序并且可以随机生成,但是没有任何东西阻止我编写这个函数,并且从我的测试来看,这适用于我测试的每个用例。

func ==<T: Encodable> (lhs: T, rhs: T) -> Bool 
   let encoder = JSONEncoder()
   do 
       let leftEncoded = try encoder.encode(lhs)
       let rightEncoded = try encoder.encode(rhs)
       return leftEncoded == rightEncoded
    catch 
       return false
   

我要解决的问题是为一个类型编写一个函数,该类型具有一个协议的数组,具有大约 20 种不同的实现,我必须实现 == 函数而不是快速自动合成。而且我知道我可以切换到 JSONSerialization.writeJSONObject 选项 .sortedKeys 以保持密钥顺序。

与任何其他编写== 函数的方法相比,此实现的缺点是什么?

【问题讨论】:

将对象编码为 JSON,然后比较两个编码的 Data 对象似乎是不必要的开销,因为您可以简单地使 T 符合 Equatable,尤其是因为 Swift 4.1 可以合成 @987654329 @大多数类型自动符合。 return try JSONEncoder().encode(lhs) == JSONEncoder().encode(rhs) @LeoDabus 这会让这个函数抛出异常。 @RobNapier 我没有说要删除do catchdo return try JSONEncoder().encode(lhs) == JSONEncoder().encode(rhs) catch print(error) return false 【参考方案1】:

答案似乎在您的问题中:“JSON 键没有任何顺序。”此外,逻辑上相等的两件事可能会以不同的方式编码(例如,如果它们的相等性仅取决于它们的 id)。此外,编码相同值的引用类型可能根本不相等(例如 UIView)。此外,这会产生一个非常广泛的 == 重载,这使得类型检查更加昂贵(就像 + 非常昂贵)。

您正在做的是尝试在可以合成时自动符合Equatable。斯威夫特本可以做到这一点;事实上,对于大多数Encodable 的情况,只需添加: Equatable 即可合成Equatable。 Swift 团队明确选择在这些情况下不自动符合并强制您在您需要时请求它,因为它并不总是意味着“所有属性都包含相同的位”。当 Swift 团队明确选择避免使用某个功能时,重新添加它并不是一个好主意。

【讨论】:

我要解决的问题是为一个类型编写一个函数,该类型具有一个协议数组,具有大约 20 种不同的实现,我必须实现 == 函数而不是快速自动合成。而且我知道我可以使用选项.sortedKeys 切换到JSONSerialization.writeJSONObject 以保持密钥顺序。您提到了== 函数的重载,是编译时还是运行时? 将此描述添加到问题中 这是一个编译时问题。您可以通过将其附加到您的协议类型而不是所有 Encodables 来实现上述目的而不会产生意外的副作用。【参考方案2】:
func ==<T: Codable> (lhs: T, rhs: T) -> Bool 
    let encoder = JSONEncoder()

    guard let lhsData = try? encoder.encode(lhs),
          let rhsData = try? encoder.encode(lhs),
          let left = try? JSONSerialization.jsonObject(with: lhsData) as? [String : Any],
          let right = try? JSONSerialization.jsonObject(with: rhsData) as? [String : Any] else  return false 

    let leftDictionary = left as NSDictionary
    let rightDictionary = right

    return leftDictionary.isEqual(to: rightDictionary)

【讨论】:

我认为guard中的第二个赋值是错误的,而不是let rhsData = try? encoder.encode(lhs)中的lhs应该是rhs

以上是关于在 Swift 中将 JSONEncoder 用于 Equatable 方法的主要内容,如果未能解决你的问题,请参考以下文章

swift JSONEncoder() 如何编码 swift NSDate/ 数据类型?

我可以取消 JSONEncoder Swift 吗?

Swift持久化存储对象到文件中JSONEncoder 与 JSONDecoder

Swift持久化存储对象到文件中JSONEncoder 与 JSONDecoder

Swift持久化存储对象到文件中JSONEncoder 与 JSONDecoder

Swift持久化存储对象到文件中JSONEncoder 与 JSONDecoder