处理 Swift 中的 NSCoding 错误
Handling NSCoding Errors in Swift
在 Swift 中应该如何处理与 NSCoding 相关的错误?
使用init?(coder:)
初始化对象时,如果数据无效,可能会初始化失败。我想捕获这些错误并适当地处理它们。为什么 init?(coder:)
在 Swift 中没有定义为抛出函数?
NSCoding
定义为可选:
init?(coder aDecoder: NSCoder)
所以肯定可以检测到错误。
90% 的 "why does Swift...?" 问题可以用 "because Cocoa." 回答 Cocoa 没有将 initWithCoder:
定义为返回错误,因此不会转换为 throws
在 Swift 中。没有办法将它干净地桥接到现有代码。 (NSCoding
返回 NeXTSTEP。我们已经构建了很多软件而没有返回 NSError
那里。并不意味着它有时可能不好,但 "couldn't init" 传统上已经足够了。 )
检查 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([=11=]) } ?? 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)
}
}
正如我所说,我从来没有在 Cocoa 程序中真正这样做过。如果存档中确实有任何内容损坏,您几乎肯定会引发 ObjC 异常,因此所有这些错误检查都可能是多余的。但它确实让你区分 "nil" 和 "not in the archive." 我只是将 属性 单独编码为 NSData
,然后对 NSData
进行编码。如果是nil
,我编码一个空的NSData
。
在 Swift 中应该如何处理与 NSCoding 相关的错误?
使用init?(coder:)
初始化对象时,如果数据无效,可能会初始化失败。我想捕获这些错误并适当地处理它们。为什么 init?(coder:)
在 Swift 中没有定义为抛出函数?
NSCoding
定义为可选:
init?(coder aDecoder: NSCoder)
所以肯定可以检测到错误。
90% 的 "why does Swift...?" 问题可以用 "because Cocoa." 回答 Cocoa 没有将 initWithCoder:
定义为返回错误,因此不会转换为 throws
在 Swift 中。没有办法将它干净地桥接到现有代码。 (NSCoding
返回 NeXTSTEP。我们已经构建了很多软件而没有返回 NSError
那里。并不意味着它有时可能不好,但 "couldn't init" 传统上已经足够了。 )
检查 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([=11=]) } ?? 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)
}
}
正如我所说,我从来没有在 Cocoa 程序中真正这样做过。如果存档中确实有任何内容损坏,您几乎肯定会引发 ObjC 异常,因此所有这些错误检查都可能是多余的。但它确实让你区分 "nil" 和 "not in the archive." 我只是将 属性 单独编码为 NSData
,然后对 NSData
进行编码。如果是nil
,我编码一个空的NSData
。