处理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

+0

这很有道理。你的观点是,它建立在现有的可可行为上是完全有效的。我认为这可能是我对“NSCoding”理解的一个问题。在Objective-C中做类似的事情我已经在try/catch块中包装了一些复杂图形的顶级对象,但是这里的错误实际上必须是我将nil值分配给非可选属性,在初始化时解码如果任何值为零,整个初始化器应该返回nil。这听起来正确吗? –

+0

正确。你需要返回nil(这也意味着,今天不幸)你必须找到* some *的方式来初始化所有的属性。你必须在返回nil之前初始化所有的东西,而Swift团队承认的东西有点坏。 –

+0

明白了。我还没有消化failable initializers,但我可以学习。你有没有处理有效的零值的建议?例如:在类型A的存档对象中有一个类型为B的可选属性。如果类型B的实例不能被初始化,它的'init?(coder:)'返回nil。对象A将使用'nil'初始化它的属性并处于有效状态。我宁愿让对象A仍然失败,因为如果图中的任何属性都失败了,它就没有意义了。那有意义吗?在这种情况下有没有办法处理? –