2017-01-08 59 views
0

当使用NSCodingNSKeyedArchiver作为父子关系时;我无法为孩子提供一个参考,因为它最终会在Swift Playground上崩溃。NSCoding - 保存父属性导致崩溃

我想在我的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类中,我想引用父类;以便将来我可以对某些父母进行测试或筛选子对象。

但是,我总是在操场上此崩溃:

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)") 
    } 
} 

回答

1

想想你的代码中正在发生什么。您归档父母。父母归档其子女。这些孩子中的每一个都试图归档其父母。然后,每个父母都试图归档其子女。然后这个循环继续并继续,直到它变得“繁荣”。

你有几个问题:

  1. 在你Child类中,parent财产必须weak。否则,你有一个参考周期和大量的内存问题。
  2. 你的Child类不应该尝试对其父进行编码/解码。
  3. 您的Parent类应在父解码时将其自己设置为每个子项的父项。

你的崩溃是由于违反问题2.

作为一个侧面说明发行3,我将重构你的代码引起的。创建孩子时不要通过父母。并且不要将children阵列直接暴露在Parent类中。我会将方法添加到Parent类中以添加和获取子项。添加子项的方法应设置添加到其中的每个子项的parent属性。

+0

好的,我会尝试重构我的代码。将父母引用为弱引用的建议是很好的建议。我没有想到这一点。我会尝试重构我的代码,看看我在哪里 – zardon

+0

我重构了我的代码。(1)将'children'设为私有(设置)var, (2)子对象不编码/解码父项 (3)函数添加子项,其中设置父项属性 (4)父属性为'weak ' 代码不再崩溃,尽管我不知道我是否做得正确。 – zardon