init?(coder:) 与 init(coder:) 的可选性。如果没有怎么办?
The optionality of init?(coder:) vs init(coder:). What to do if nil?
NSCoding 需要 init(coder:)
,但也有此方法的可选版本 init?(coder:)
。
如果这个returns nil
到底应该怎么办?这甚至是个问题吗?
假设您正在使用 init(coder:)
初始化大型对象层次结构,每个对象的子对象本身使用 init?(coder:)
进行初始化。如果其中一个对象是 nil
,应用程序不会崩溃吗?父对象不期望一个 nil 子对象。
它甚至对 "init nil" 意味着什么?
class Parent: NSCoding {
var children: [Child]
required init?(coder aDecoder: NSCoder) {
guard let children = aDecoder.decodeObject(forKey: "children") as? [Child] else { return nil }
self.children = children
}
}
class Child: NSCoding {
var name: String
required init?(coder aDecoder: NSCoder) {
guard let name = aDecoder.decodeObject(forKey: "name") as? String else { return nil }
self.name = name
}
}
一个策略是简单地 return 一个新实例,而不是简单地 returning nil
。数据会丢失,但应用程序会 运行.
你最好不要return nil
.
作为我在 Xcode 8.3.2 (8E2002) 中的测试,return nil
在 init(coder:)
中导致 NSKeyedUnarchiver.unarchiveObject
崩溃或 return意想不到的结果。
准备一个 class 编码错误的数据类型 "test2":
class MyClass: NSObject, NSCoding {
var x: String
init(_ x: String) {
self.x = x
}
required init?(coder aDecoder: NSCoder) {
guard let x = aDecoder.decodeObject(forKey: "x") as? String else {
return nil
}
self.x = x
}
func encode(with aCoder: NSCoder) {
if x == "test2" {
aCoder.encode(Int(4), forKey: "x")
} else {
aCoder.encode(x, forKey: "x")
}
}
}
TestCaseA: 归档包含上述内容的字典 MyClass
,然后取消归档。
结果:在 NSKeyedUnarchiver.unarchiveObject
.
上崩溃
let encodedData = NSKeyedArchiver.archivedData(withRootObject: [
"k1":MyClass("test1"),
"k2":MyClass("test2"),
"k3":"normal things"
])
UserDefaults.standard.set(encodedData, forKey: "xx")
if let data = UserDefaults.standard.data(forKey: "xx"),
let _data = NSKeyedUnarchiver.unarchiveObject(with: data) {
if let dict = _data as? [String:Any] {
debugPrint(dict.count)
}
}
TestCaseB: 归档一个包含上述MyClass
的数组,然后取消归档。
结果: return 一个空数组(但预期是一个包含 1 个元素的数组)
let encodedData = NSKeyedArchiver.archivedData(withRootObject: [
MyClass("test1"),
MyClass("test2")
])
UserDefaults.standard.set(encodedData, forKey: "xx")
if let data = UserDefaults.standard.data(forKey: "xx"),
let _data = NSKeyedUnarchiver.unarchiveObject(with: data) {
if let dict = _data as? [Any] {
debugPrint(dict.count)
}
}
NSCoding 需要 init(coder:)
,但也有此方法的可选版本 init?(coder:)
。
如果这个returns nil
到底应该怎么办?这甚至是个问题吗?
假设您正在使用 init(coder:)
初始化大型对象层次结构,每个对象的子对象本身使用 init?(coder:)
进行初始化。如果其中一个对象是 nil
,应用程序不会崩溃吗?父对象不期望一个 nil 子对象。
它甚至对 "init nil" 意味着什么?
class Parent: NSCoding {
var children: [Child]
required init?(coder aDecoder: NSCoder) {
guard let children = aDecoder.decodeObject(forKey: "children") as? [Child] else { return nil }
self.children = children
}
}
class Child: NSCoding {
var name: String
required init?(coder aDecoder: NSCoder) {
guard let name = aDecoder.decodeObject(forKey: "name") as? String else { return nil }
self.name = name
}
}
一个策略是简单地 return 一个新实例,而不是简单地 returning nil
。数据会丢失,但应用程序会 运行.
你最好不要return nil
.
作为我在 Xcode 8.3.2 (8E2002) 中的测试,return nil
在 init(coder:)
中导致 NSKeyedUnarchiver.unarchiveObject
崩溃或 return意想不到的结果。
准备一个 class 编码错误的数据类型 "test2":
class MyClass: NSObject, NSCoding {
var x: String
init(_ x: String) {
self.x = x
}
required init?(coder aDecoder: NSCoder) {
guard let x = aDecoder.decodeObject(forKey: "x") as? String else {
return nil
}
self.x = x
}
func encode(with aCoder: NSCoder) {
if x == "test2" {
aCoder.encode(Int(4), forKey: "x")
} else {
aCoder.encode(x, forKey: "x")
}
}
}
TestCaseA: 归档包含上述内容的字典 MyClass
,然后取消归档。
结果:在 NSKeyedUnarchiver.unarchiveObject
.
let encodedData = NSKeyedArchiver.archivedData(withRootObject: [
"k1":MyClass("test1"),
"k2":MyClass("test2"),
"k3":"normal things"
])
UserDefaults.standard.set(encodedData, forKey: "xx")
if let data = UserDefaults.standard.data(forKey: "xx"),
let _data = NSKeyedUnarchiver.unarchiveObject(with: data) {
if let dict = _data as? [String:Any] {
debugPrint(dict.count)
}
}
TestCaseB: 归档一个包含上述MyClass
的数组,然后取消归档。
结果: return 一个空数组(但预期是一个包含 1 个元素的数组)
let encodedData = NSKeyedArchiver.archivedData(withRootObject: [
MyClass("test1"),
MyClass("test2")
])
UserDefaults.standard.set(encodedData, forKey: "xx")
if let data = UserDefaults.standard.data(forKey: "xx"),
let _data = NSKeyedUnarchiver.unarchiveObject(with: data) {
if let dict = _data as? [Any] {
debugPrint(dict.count)
}
}