处理Swift中的NSCoding错误

时间:2015-10-16 18:02:10

标签: swift nscoding

如何在Swift中处理与NSCoding相关的错误?

使用init?(coder:)初始化对象时,如果数据无效,则可能无法初始化。我想抓住这些错误并妥善处理它们。为什么init?(coder:)未在Swift中定义为抛出函数?

1 个答案:

答案 0 :(得分:5)

NSCoding将其定义为可选:

init?(coder aDecoder: NSCoder)

因此,检测错误当然是可能的。

90%的“为什么Swift ......?”问题可以用“因为可可”来回答。 Cocoa没有将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)
    }
}

正如我所说,我从来没有在Cocoa计划中真正做到这一点。如果存档中的任何内容确实已损坏,您几乎肯定会最终提出一个ObjC异常,所以所有这些错误检查都可能有点过分。但它确实让你区分“无”和“不存档”。我只是将属性单独编码为NSData,然后对NSData进行编码。如果是nil,我会对空NSData进行编码。