我有这些类:
import UIKit
class Report: Codable {
var name: String = ""
var budgetLines: [ReportLines] = []
init() {
self.name = "My Report"
self.budgetLines = [ReportLines(name: "Test1", parent: self), ReportLines(name: "Test2", parent: self), ReportLines(name: "Test3", parent: self)]
}
}
class ReportLines: Codable {
var name: String
weak var parent: Report?
init(name: String, parent: Report) {
self.name = name
self.parent = parent
}
}
let report = Report()
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let archiveURL = documentsDirectory.appendingPathComponent("Report").appendingPathExtension("plist")
let propertyListEncoder = PropertyListEncoder()
let encodedReport = try? propertyListEncoder.encode(report)
try? encodedReport?.write(to: archiveURL, options: .noFileProtection)
重要细节:ReportLines
类包含指向拥有Report
类的指针。这个想法是人们可以编写类似的东西:
myReportLine.parent!.name
当我尝试将其保存为plist时,编码操作会挂起。我怀疑是这种情况,因为它试图编码一个对象,该对象包含一个包含指向外部对象的指针的对象数组。 Codable可能会进入循环。
我该如何防止这种情况?
答案 0 :(得分:2)
我找到了这个解决方案:
import UIKit
class Report: Codable {
var name: String = ""
var reportLines: [ReportLine] = []
enum ReportCodingKeys: String, CodingKey {
case name
case reportLines
}
init() {
self.name = "My Report"
}
required init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: ReportCodingKeys.self)
name = try values.decode(String.self, forKey: .name)
reportLines = try values.decode([ReportLine].self, forKey: .reportLines)
for reportLine in reportLines {
reportLine.parent = self
}
}
func addReportLine(name: String) {
reportLines.append(ReportLine(name: name, parent: self))
}
}
class ReportLine: Codable {
var name: String
weak var parent: Report?
enum CodingKeys: String, CodingKey {
case name
}
init(name: String, parent: Report) {
self.name = name
self.parent = parent
}
}
let report = Report()
report.addReportLine(name: "Test1")
report.addReportLine(name: "Test2")
report.addReportLine(name: "Test3")
// Saving
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let archiveURL = documentsDirectory.appendingPathComponent("Report").appendingPathExtension("plist")
let propertyListEncoder = PropertyListEncoder()
var encodedReport = try? propertyListEncoder.encode(report)
try? encodedReport?.write(to: archiveURL, options: .noFileProtection)
// Retrieving
let propertyListDecoder = PropertyListDecoder()
encodedReport = try? Data(contentsOf: archiveURL)
let report2 = try? propertyListDecoder.decode(Report.self, from: encodedReport!)
// Comparing
print("\(report.reportLines[2].name ?? "nil")")
print("report!.reportLines[0].parent: \(String(describing: report.reportLines[2].parent?.name ?? "nil"))")
print("\(report2?.reportLines[2].name ?? "nil")")
print("report2!.reportLines[0].parent: \(String(describing: report2?.reportLines[2].parent?.name ?? "nil"))")
我唯一问自己:是否有更好的方法来调用默认解码器而不是自己提供init(来自:)。
希望这有用。