显然,CLLocation 对象不能精确地归档/取消归档

Apparently, CLLocation objects cannot be archived / unarchived precisely

在我的应用程序中(仅显示相关代码),我有一个 class Test 和 属性

var location: CLLocation  

我用

存档
public func encode(with aCoder: NSCoder) {
    aCoder.encode(location, forKey: "location")
}  

并且使用

取消存档
required convenience public init?(coder aDecoder: NSCoder) {
    let unarchivedLocation = aDecoder.decodeObject(forKey: "location") as! CLLocation
    self.init(location: unarchivedLocation)
}  

使用

完成单元测试
func test_archiningUnarchiving() {
    // given
    let location = CLLocation.init(latitude: 0.0, longitude: 0.0)
    let test = Test(location: location)
    // when
    let data = NSKeyedArchiver.archivedData(withRootObject: test)
    let unarchivedTest = NSKeyedUnarchiver.unarchiveObject(with: data) as? Test
    // then
    XCTAssertEqual(unarchivedTest!.location, location, "location was not correctly unarchived")
}  

此测试失败:

XCTAssertEqual failed: ("<+0.00000000,+0.00000000> +/- 0.00m (speed -1.00 mps / course -1.00) @ 1/19/18, 3:30:50 PM Central European Standard Time") is not equal to ("<+0.00000000,+0.00000000> +/- 0.00m (speed -1.00 mps / course -1.00) @ 1/19/18, 3:30:50 PM Central European Standard Time") - location was not correctly unarchived

日志两次显示原始位置和未存档位置完全相同的数据。
知道哪里出了问题吗??

问题不在于归档,而是平等测试。如果你比较两个不同的 CLLocation 实例,即使它们是相同的,它也总是 return false.

最重要的是,任何未明确实现 isEqual:NSObject 子类(例如 CLLocation 的情况)都会遇到此行为。

Using Swift with Cocoa and Objective-C: Interacting with Objective-C APIs 说:

Swift provides default implementations of the == and === operators and adopts the Equatable protocol for objects that derive from the NSObject class. The default implementation of the == operator invokes the isEqual: method ... You should not override the equality or identity operators for types imported from Objective-C.

而且,Concepts in Objective-C Programming: Introspection 告诉我们:

The default NSObject implementation of isEqual: simply checks for pointer equality.

就个人而言,我希望 NSObject 子类不会自动继承 isEqual:,但事实就是如此。

底线,不要尝试测试 NSObject 子类的相等性,除非您知道它已正确实现 isEqual: 覆盖。如果你愿意,写你自己的方法,例如isEqual(to location: CLLocation)(但不是 isEqual(_:))执行两个 CLLocation 对象的成员比较。