Swift 可编码循环

Swift 可编码循环,swift,codable,Swift,Codable,我有以下类型的课程: 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: "

我有以下类型的课程:

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
类包含指向所属
报告的指针。我们的想法是可以编写如下代码:

myReportLine.parent!.name
当我试图将其保存到plist中时,编码操作挂起。我怀疑是这样的,因为它试图对一个对象进行编码,该对象包含一个包含指向外部对象指针的对象数组。Codable可能会进入一个循环

如何防止这种情况发生?

我找到了以下解决方案:

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(from:)


希望这有帮助。

好主意!您还可以通过执行
reportLines.forEach(){$0.parent=self}
来缩短它,因为您有一个循环依赖项(parent)——这就是它导致循环的原因。只有手动可编码实现才能解决这个问题。