NSCoding - 保存 parent 属性导致崩溃
NSCoding - Save the parent attribute causes a crash
当对 parent-child 关系使用 NSCoding
或 NSKeyedArchiver
时;我无法在 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)")
}
}
在Child
class中我要参考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".
您有几个问题:
- 在您的
Child
class 中,parent
属性 需要 weak
。否则你会遇到引用循环和大量内存问题。
- 您的
Child
class 不应尝试 encode/decode 其 parent。
- 当 parent 被解码时,您的
Parent
class 应该将自己设置为每个 child 的 parent。
您的崩溃是由于违反问题 2 造成的。
作为问题 3 的旁注,我将重构您的代码。创建 child 时不要传递 parent。并且不要直接在 Parent
class 中公开 children
数组。我会在 Parent
class 中添加方法来添加和获取 children。添加children的方法应该设置每个child添加的parent
属性。
当对 parent-child 关系使用 NSCoding
或 NSKeyedArchiver
时;我无法在 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)")
}
}
在Child
class中我要参考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".
您有几个问题:
- 在您的
Child
class 中,parent
属性 需要weak
。否则你会遇到引用循环和大量内存问题。 - 您的
Child
class 不应尝试 encode/decode 其 parent。 - 当 parent 被解码时,您的
Parent
class 应该将自己设置为每个 child 的 parent。
您的崩溃是由于违反问题 2 造成的。
作为问题 3 的旁注,我将重构您的代码。创建 child 时不要传递 parent。并且不要直接在 Parent
class 中公开 children
数组。我会在 Parent
class 中添加方法来添加和获取 children。添加children的方法应该设置每个child添加的parent
属性。