处理Swift中的NSCoding错误
Swift中应该如何处理与NSCoding相关的错误?处理Swift中的NSCoding错误
当使用init?(coder:)
对对象进行初始化时,如果数据无效,则可能无法初始化对象。我想抓住这些错误并妥善处理它们。为什么init?(coder:)
没有在Swift中定义为抛出函数?
NSCoding
将其定义为可选:
init?(coder aDecoder: NSCoder)
所以它肯定是可以检测的错误。
90%的“为什么Swift ...?”问题可以用“因为可可”来解答。可可并没有将initWithCoder:
定义为返回一个错误,所以它不会在Swift中转换为throws
。没有办法将它与现有代码完美地结合在一起。 (NSCoding
追溯到NeXTSTEP的,因为我们已经有很多软件不返回NSError
那里。这并不意味着它可能不是很好的时候,但是“无法初始化”已经够传统)
检查nil
。这意味着某些失败。这是提供的所有信息。
我从来没有在实践中必须检查太深,整个对象图是正确的。如果不是,那么你很可能会得到其他错误,并且记住NSKeyedUnarchiver
如果解码失败将会引发一个ObjC异常(!!!)。除非你将它包装在ObjC @catch
中,否则无论如何你都会崩溃。 (是的,这是非常疯狂的,但仍然如此。)
但是,如果我想非常小心,并确保我期望在档案中的东西真的在档案中(即使它们是nil
),我可能会做这种方式(未经测试,它编译,但我还没有确定它确实有效):
import Foundation
enum DecodeError: ErrorType {
case MissingProperty(String)
case MalformedProperty(String)
}
extension NSCoder {
func encodeOptionalObject(obj: AnyObject?, forKey key: String) {
let data = obj.map{ NSKeyedArchiver.archivedDataWithRootObject($0) } ?? NSData()
self.encodeObject(data, forKey: key)
}
func decodeRequiredOptionalObjectForKey(key: String) throws -> AnyObject? {
guard let any = self.decodeObjectForKey(key) else {
throw DecodeError.MissingProperty(key)
}
guard let data = any as? NSData else {
throw DecodeError.MalformedProperty(key)
}
if data.length == 0 {
return nil // Found nil
}
// But remember, this will raise an ObjC exception if it's malformed data!
guard let prop = NSKeyedUnarchiver.unarchiveObjectWithData(data) else {
throw DecodeError.MalformedProperty(key)
}
return prop
}
}
class MyClass: NSObject, NSCoding {
static let propertyKey = "property"
let property: String?
init(property: String?) {
self.property = property
}
required init?(coder aDecoder: NSCoder) {
do {
property = try aDecoder.decodeRequiredOptionalObjectForKey(MyClass.propertyKey) as? String
} catch {
// do something with error if you want
property = nil
super.init()
return nil
}
super.init()
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeOptionalObject(property, forKey: MyClass.propertyKey)
}
}
正如我所说的,在可可节目,我从来没有真正做到了这一点。如果存档中的任何内容真的损坏了,那么几乎肯定会结束引发ObjC异常的情况,所以所有这些错误检查都可能是过度的。但它确实可以让你区分“无”和“不在档案中”。我只是编码属性单独作为NSData
,然后编码NSData
。如果它是nil
,我编码一个空的NSData
。
这很有道理。你的观点是,它建立在现有的可可行为上是完全有效的。我认为这可能是我对“NSCoding”理解的一个问题。在Objective-C中做类似的事情我已经在try/catch块中包装了一些复杂图形的顶级对象,但是这里的错误实际上必须是我将nil值分配给非可选属性,在初始化时解码如果任何值为零,整个初始化器应该返回nil。这听起来正确吗? –
正确。你需要返回nil(这也意味着,今天不幸)你必须找到* some *的方式来初始化所有的属性。你必须在返回nil之前初始化所有的东西,而Swift团队承认的东西有点坏。 –
明白了。我还没有消化failable initializers,但我可以学习。你有没有处理有效的零值的建议?例如:在类型A的存档对象中有一个类型为B的可选属性。如果类型B的实例不能被初始化,它的'init?(coder:)'返回nil。对象A将使用'nil'初始化它的属性并处于有效状态。我宁愿让对象A仍然失败,因为如果图中的任何属性都失败了,它就没有意义了。那有意义吗?在这种情况下有没有办法处理? –