arrays - NSCoding - 保存父属性导致崩溃

标签 arrays swift nscoding nskeyedarchiver

当使用 NSCodingNSKeyedArchiver 建立父子关系时;我无法在子项中设置对父项的引用,因为它最终会在 Swift Playground 上崩溃。

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

但在将数据加载回内存时,它最终会在 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)")
    }
}

Child 类中,我想引用父类;以便将来我可以与某些 parent 一起对子对象进行测试或过滤。

但是,我总是在 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.

它似乎在引用父对象时崩溃。

如何确保 NSCoding 将我的父属性保存在子对象中?

非常感谢


编辑:重构代码

这段重构代码似乎可以工作,但我不知道我是否做对了。

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 存档其 child 。然后,这些 child 中的每一个都试图存档其 parent 。然后每个 parent 都试图存档其 child 。这个循环然后继续下去,直到它“繁荣”。

你有几个问题:

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

您的崩溃是由于违反问题 2 导致的。

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

关于arrays - NSCoding - 保存父属性导致崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41538831/

相关文章:

arrays - 如何在 R 中提取 1x1 数组切片作为矩阵?

ios - 如何选择范围 - Swift 中的 FSCalendar?

Swift:尝试实现 NSCoding

objective-c - 如何使用 NSCoding 加载 Objective C Singleton?

iOS 对象归档从头到尾

java - 如何在不更改名称的情况下在 Java 中扩展数组

java - 在 prepare 语句中传递数组参数 - 获取 "java.sql.SQLFeatureNotSupportedException"

javascript - 将 php 数组导入 javascript 数据集

ios - Alamofire 在 swift 5 中响应无效的 json

ios - 按下时快速更改按钮背景颜色和图像颜色