加载数据后不符合 Equatable 的 Swift 对象
Posted
技术标签:
【中文标题】加载数据后不符合 Equatable 的 Swift 对象【英文标题】:Swift Objects not conforming to Equatable after loading data 【发布时间】:2019-11-23 16:21:12 【问题描述】:从 plist 加载后,我遇到的对象似乎不符合 Equatable
协议的问题。一些背景知识:我有一个对象 TransmitterList
类型,它包含一个 Transmitters 类型的数组。在第一次启动时,一切正常,我可以通过调用删除项目
if let index = transmitterList.transmitters.firstIndex(of: transmitter)
transmitterList.transmitters.remove(at: index)
此方法在第一次运行时完美运行,问题出现在应用程序数据已保存并在终止应用程序后重新加载后。所有保存和加载都是使用Codable
协议和 Plist 编码器和解码器完成的。发射器列表中的发射器仍然存在,但在尝试删除发射器时,对firstIndex(of:)
的调用返回nil
。其他依赖Equatable
协议的方法也无法正常运行。似乎已保存的发射器不再符合Equatable
。但是,在重新启动后添加新发射器时,我能够成功删除任何新添加的发射器。
我能够通过比较一些发射器属性并以这种方式删除它们来创建一个解决方法,但它似乎不是很强大。真的想了解发射机不再符合Equatable
的根本原因。
感谢您的帮助,谢谢!
更新了保存和加载代码。
这是我正在使用的 dataModel 对象,它只是保存了一个 TransmitterLists 数组。 Transmitter 类中没有自定义编码或解码,它只是符合Codable
,没有任何额外的方法。
class DataModel
// MARK: - Properties
var transmitterLists = [TransmitterList]()
// MARK: - Initialization
init()
loadData()
// MARK: - Data Persistence
func documentsDirectory() -> URL
let paths = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask)
return paths[0]
func dataFilePath() -> URL
return documentsDirectory().appendingPathComponent("FrequencyCoordinator.plist")
func saveData()
let encoder = PropertyListEncoder()
do
let data = try encoder.encode(transmitterLists)
try data.write(to: dataFilePath(), options: Data.WritingOptions.atomic)
print("DATA SAVED!!")
catch
print("That did not work: \(error.localizedDescription)")
func loadData()
let path = dataFilePath()
if let data = try? Data(contentsOf: path)
let decoder = PropertyListDecoder()
do
print("DATA LOADED")
transmitterLists = try decoder.decode([TransmitterList].self, from: data)
catch
print("That didnt work ;-< \(error.localizedDescription)")
在 Transmitter 类中添加。任何其他类型也符合Codable
,但只能在定义中声明。也许在确保类型正确符合方面还有更多工作要做?
class Transmitter: Equatable, Codable
static func == (lhs: Transmitter, rhs: Transmitter) -> Bool
return lhs.id == rhs.id
var id: Int
var name: String
var transmitterType: TransmitterType
var block: Block
var frequency: Frequency
var doesOverlap: Bool = false
var transmitterOverlapRange: ClosedRange<Double>
var imProductOverlapRanges = [ClosedRange<Double>]()
var overlapsWith = [Transmitter]()
var doesHaveTwoTransmitterIMInterference = false
var doesHaveThreeTransmitterIMInterference = false
var twoTransmitterIMProductsOverlap = [TwoTransmitterIMProduct]()
var threeTransmitterIMProductsOverlap = [ThreeTransmitterIMProduct]()
var isDisabled = false
var frequencyIsSet: Bool
get
self.frequency.literal != 0.0
init(name: String, type: TransmitterType, block: Block, frequency: Frequency, transmitterOverlapRange: ClosedRange<Double>)
self.id = DataModel.nextTransmitterID()
self.name = name
self.transmitterType = type
self.block = block
self.frequency = frequency
self.transmitterOverlapRange = transmitterOverlapRange
enum TransmitterType: Int, Codable
case talent
case hops
case ifb
【问题讨论】:
“真的很想了解发射机不再符合 Equatable 的根本原因。”它们确实符合 Equatable。但是根据您对此对象类型的 Equatable 定义,它们不再相等。 我想你是对的。虽然我不完全确定在保存数据和重新加载数据之间发生了什么变化。这个问题也对应用程序的其他部分造成了严重破坏。已保存和加载的发射机似乎不再能够更改其频率。所有标签都会根据新频率进行相应更新,但打印声明在幕后显示发射器的频率实际上并未改变。这些问题只有在保存和加载后才会出现。 您是否对发射器对象使用自定义方法.init(decoder:)
和encode(encoder:)
?如果是这样,您可以发布它们,因为我怀疑您的解码方式与编码方式不同。
@flanker 使用保存和加载方法更新了原始问题。
相关的不是加载/保存方法,而是 TransmitterList(和任何子类型)的编码/解码方式。 IE。如何使它们符合 Codable。您可以将 Transmitter 类添加到问题中吗?
【参考方案1】:
您仍然没有显示相关代码。你在抱怨
if let index = transmitterList.transmitters.firstIndex(of: transmitter)
失败,但您没有显示该行在代码中的位置,也没有告诉我们transmitter
是什么或transmitterList
中的内容。
但是,您的表现足以让您猜测一下。您重新定义了 Transmitter 的相等性,使得两个 Transmitter 当且仅当它们的 id
值相等时才相等。
static func == (lhs: Transmitter, rhs: Transmitter) -> Bool
return lhs.id == rhs.id
因此我们可以在这一行得出结论:
if let index = transmitterList.transmitters.firstIndex(of: transmitter)
...您的transmitter
与列表中的任何发射器都有不同的id
。如果这是一个新的transmitter
,这将是有意义的,因为它将使用新的id
生成。
确实,你说:
我能够通过比较一些发射器属性并以这种方式删除它们来创建一个解决方法,但它似乎不是很强大
确实没有。但这表明您似乎期望一个 Transmitter 基于“其某些属性”与另一个 Transmitter 等同。它不是。您的唯一平等点是id
。因此,我们可以得出结论,您正在尝试在其他发射器列表中找到具有唯一 id
的 transmitter
— 当然它不存在,因为根据定义,唯一的 id
是唯一的。
【讨论】:
解决您的几点问题。我遇到的主要问题是,只有在保存并重新加载数据后,对firstIndex(of:)
的调用才会失败。在应用程序第一次运行时,在调用任何保存或加载方法之前,一切正常。我试图获取索引的Transmitter
是我试图从TransmitterList.transmitters
数组中删除的那个。为了解决这个问题,我最初比较了Transmitter.name
属性,以便能够从阵列中移除一个发射器,然后继续为每个Transmitter
提供一个唯一的ID。以上是关于加载数据后不符合 Equatable 的 Swift 对象的主要内容,如果未能解决你的问题,请参考以下文章