NSCoding - 保存 parent 属性导致崩溃

NSCoding - Save the parent attribute causes a crash

当对 parent-child 关系使用 NSCodingNSKeyedArchiver 时;我无法在 child 中设置对 parent 的引用,因为它最终会在 Swift 游乐场上崩溃。

我想在我的 Child class 中引用我的 Parent class。

但在将数据加载回内存时,它最终会在 Playground 上崩溃。

class Parent: NSObject, NSCoding {
    var children:[Child] = [Child]()

    init(children:[Child]?) {
        if let childrenList = children {
            self.children = childrenList
        }
    }

    public convenience required init?(coder aDecoder: NSCoder) {
        let children = aDecoder.decodeObject(forKey: "children") as! [Child]

        self.init(children: children)
    }

    func encode(with aCoder: NSCoder) {
        aCoder.encode(children, forKey:"children")
    }

}

class Child: NSObject, NSCoding {
    var parent: Parent

    init(parent:Parent) {
        self.parent = parent
    }

    public convenience required init?(coder aDecoder: NSCoder) {
        let parent = aDecoder.decodeObject(forKey: "parent") as! Parent

        self.init(parent: parent)
    }

    func encode(with aCoder: NSCoder) {
        aCoder.encode(parent, forKey:"parent")
    }
}

var parent1 = Parent.init(children: nil)
var parent2 = Parent.init(children: nil)
var child1 = Child.init(parent: parent1)
var child2 = Child.init(parent: parent2)
parent1.children.append(child1)
parent2.children.append(child2)

let parents = [parent1, parent2]


let manager = FileManager.default
let url = manager.urls(for: .documentDirectory, in: .userDomainMask).first! as URL
let writeFile: URL = url.appendingPathComponent("sample.data")

print ("Attempting to write to: \(writeFile.path)")


NSKeyedArchiver.archiveRootObject(parents, toFile: writeFile.path)

// Crash occurs here.
if let parentData = NSKeyedUnarchiver.unarchiveObject(withFile: writeFile.path) as? [Parent] {
    for p in parentData {
        print ("\(p.children.count)")
    }
}

Childclass中我要参考parent;以便将来我可以对 child objects 和某些 parents.

进行测试或过滤

但是,我总是在 Playground 上遇到此崩溃:

Playground execution aborted: error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x10). The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.

似乎在引用 parent object 时崩溃。

如何确保 NSCoding 将我的 parent 属性保存在 child object 中?

非常感谢


编辑:重构代码

此重构代码似乎有效,但我不知道我是否做对了。

class Parent: NSObject, NSCoding {
    private (set) var children:[Child] = [Child]()

    override init() {
        super.init()
    }

    public convenience required init?(coder aDecoder: NSCoder) {
        let children = aDecoder.decodeObject(forKey: "children") as! [Child]

        self.init()
        self.createChildren(children: children)
    }

    func encode(with aCoder: NSCoder) {
        aCoder.encode(children, forKey:"children")
    }

    func addChild(child:Child) {
        child.parent = self
        self.children.append(child)
    }

    private func createChildren(children:[Child]) {
        for child:Child in children {
            self.addChild(child: child)
        }
    }
}

class Child: NSObject, NSCoding {
    weak var parent: Parent?

    override init() {
        super.init()
    }

    public convenience required init?(coder aDecoder: NSCoder) {
        self.init()
    }

    func encode(with aCoder: NSCoder) {
    }
}

var parent1 = Parent.init()
var parent2 = Parent.init()
var child1 = Child.init()
var child2 = Child.init()
parent1.addChild(child: child1)
parent2.addChild(child: child2)
let parents = [parent1, parent2]


let manager = FileManager.default
let url = manager.urls(for: .documentDirectory, in: .userDomainMask).first! as URL
let writeFile: URL = url.appendingPathComponent("sample.data")

print ("Attempting to write to: \(writeFile.path)")


NSKeyedArchiver.archiveRootObject(parents, toFile: writeFile.path)


if let parentData = NSKeyedUnarchiver.unarchiveObject(withFile: writeFile.path) as? [Parent] {
    for p in parentData {
        print ("\(p.children.count)")
    }
}

仔细考虑您的代码中当前发生的事情。你存档一个parent。 parent存档其children。这些 children 中的每一个都会尝试归档其 parent。然后每个 parents 尝试存档其 children。然后这个循环一直持续到 "boom".

您有几个问题:

  1. 在您的 Child class 中,parent 属性 需要 weak。否则你会遇到引用循环和大量内存问题。
  2. 您的 Child class 不应尝试 encode/decode 其 parent。
  3. 当 parent 被解码时,您的 Parent class 应该将自己设置为每个 child 的 parent。

您的崩溃是由于违反问题 2 造成的。

作为问题 3 的旁注,我将重构您的代码。创建 child 时不要传递 parent。并且不要直接在 Parent class 中公开 children 数组。我会在 Parent class 中添加方法来添加和获取 children。添加children的方法应该设置每个child添加的parent 属性。