在 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 catch
。 do 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/ 数据类型?
Swift持久化存储对象到文件中JSONEncoder 与 JSONDecoder
Swift持久化存储对象到文件中JSONEncoder 与 JSONDecoder