Swift只能防止NSKeyedUnarchiver.decodeObject崩溃吗? [英] Swift only way to prevent NSKeyedUnarchiver.decodeObject crash?

查看:1217
本文介绍了Swift只能防止NSKeyedUnarchiver.decodeObject崩溃吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

NSKeyedUnarchiver.decodeObject 将导致崩溃/ SIGABRT 。我已经看到解决这个问题的唯一解决方案可以追溯到Swift的早期历史,并且需要使用Objective C(也是使用Swift 2实现的 guard 抛出尝试& catch )。我可以找出Objective C路线 - 但如果可能的话,我更愿意理解一个仅限Swift的解决方案。

NSKeyedUnarchiver.decodeObject will cause a crash / SIGABRT if the original class is unknown. The only solution I have seen to catching this issue dates from Swift's early history and required using Objective C (also pre-dated Swift 2's implementation of guard, throws, try & catch). I could figure out the Objective C route - but I would prefer to understand a Swift-only solution if possible.

例如 - 数据已用<$ c编码$ C> NSPropertyListFormat.XMLFormat_v1_0 。如果编码数据的类未知,则以下代码将在 unarchiver.decodeObject()处失败。

For example - the data has been encoded with NSPropertyListFormat.XMLFormat_v1_0. The following code will fail at unarchiver.decodeObject() if the class of the encoded data is unknown.

//...
let dat = NSData(contentsOfURL: url)!
let unarchiver = NSKeyedUnarchiver(forReadingWithData: dat)

//it will crash after this if the class in the xml file is not known

if let newListCollection = (unarchiver.decodeObject()) as? List {
    return newListCollection
} else {
    return nil
}
//...

在寻找 .decodeObject 之前,我正在寻找一种Swift 2测试数据是否有效的方法 - 因为 .decodeObject 没有抛出 - 这意味着尝试 - catch 似乎不是Swift中的一个选项(没有 throws 的方法不能包装AFAIK)。或者另一种解码数据的替代方法,如果解码失败,我将捕获错误。我希望用户能够从iCloud驱动器或Dropbox导入文件 - 因此需要对其进行适当的验证。我不能假设编码数据是安全的。

I am looking for a Swift 2 only way to test whether the data is valid before attempting .decodeObject - since .decodeObject has no throws - which means that try - catch does not seem to be an option in Swift (methods without throws cannot be wrapped AFAIK). Or else an alternative way of decoding the data which will throw an error I can catch if the decode fails. I want the user to be able to import a file from iCloud drive or Dropbox - therefore it needs to be properly validated. I cannot assume that the encoded data is safe.

NSKeyedUnarchiver 方法 .unarchiveTopLevelObjectWithData & .validateValue 都有 throws 。是否有某些方法可以使用这些?我无法弄清楚如何在这种情况下开始尝试实现 validateValue 。这甚至是一条可能的路线吗?或者我应该寻找解决方案的其他方法之一?

The NSKeyedUnarchiver methods .unarchiveTopLevelObjectWithData & .validateValue both have throws. Is there perhaps some way that these could be used? I cannot work out how to even begin to attempt to implement validateValue in this context. Is this even a possible route? Or should I be looking to one of the other methods for a solution?

或者有人知道另一种Swift 2解决此问题的方法吗?我相信我感兴趣的关键字可能是 $ classname - 但是TBH我在试图弄清楚如何实现<$ c $方面不够深入c> validateValue - 甚至是否是坚持下去的正确途径。我觉得我错过了一些明显的东西。

Or does anyone know an alternative Swift 2 only way of addressing this issue? I believe that the key I am interested in is probably entitled $classname - but TBH I am out of my depth with respect to trying to work out how to implement validateValue - or even whether that would be the correct route to persevere with. I have the sense that I am missing something obvious.

编辑:这是一个解决方案 - 感谢rintaro的很好的答案

最初的答案为我解决了问题 - 即实施代表。

The initial answer solved the issue for me - i.e. implementing a delegate.

现在我已经选择了rintaro附加编辑回复的解决方案,如下所示:

For now however I have gone with a solution built around rintaro's additional edited response as follows:

//...
let dat = NSData(contentsOfURL: url)!
let unarchiver = NSKeyedUnarchiver(forReadingWithData: dat)

do {
    let decodedDataObject = try unarchiver.decodeTopLevelObject()
    if let newListCollection = decodedDataObject as? List {
        return newListCollection
    } else {
        return nil
    }
}
catch {
    return nil
}
//...


推荐答案

当<$ c时$ c> NSKeyedUnarchiver 遇到未知类, unarchiver(_:cannotDecodeObjectOfClassName:originalClasses:) 委托方法被调用。

When NSKeyedUnarchiver encounters unknown classes, unarchiver(_:cannotDecodeObjectOfClassName:originalClasses:) delegate method is called.


例如,委托可以加载一些代码以将类引入运行时并返回类,或者替换不同的类对象即可。如果委托返回 nil ,则取消归档中止并且该方法引发 NSInvalidUnarchiveOperationException

The delegate may, for example, load some code to introduce the class to the runtime and return the class, or substitute a different class object. If the delegate returns nil, unarchiving aborts and the method raises an NSInvalidUnarchiveOperationException.

因此,您可以像这样实现委托:

So, you can implement the delegate like this:

class MyUnArchiverDelegate: NSObject, NSKeyedUnarchiverDelegate {

    // This class is placeholder for unknown classes.
    // It will eventually be `nil` when decoded.
    final class Unknown: NSObject, NSCoding  {
        init?(coder aDecoder: NSCoder) { super.init(); return nil }
        func encodeWithCoder(aCoder: NSCoder) {}
    }

    func unarchiver(unarchiver: NSKeyedUnarchiver, cannotDecodeObjectOfClassName name: String, originalClasses classNames: [String]) -> AnyClass? {
        return Unknown.self
    }
}

然后:

let unarchiver = NSKeyedUnarchiver(forReadingWithData: dat)
let delegate = MyUnArchiverDelegate()
unarchiver.delegate = delegate

unarchiver.decodeObjectForKey("root")
// -> `nil` if the root object is unknown class.






ADDED

我没有注意到 NSCoder 扩展名更快捷的方法:

I didn't noticed that NSCoder has extension with more swifty methods:

extension NSCoder {
    @warn_unused_result
    public func decodeObjectOfClass<DecodedObjectType : NSCoding where DecodedObjectType : NSObject>(cls: DecodedObjectType.Type, forKey key: String) -> DecodedObjectType?
    @warn_unused_result
    @nonobjc public func decodeObjectOfClasses(classes: NSSet?, forKey key: String) -> AnyObject?
    @warn_unused_result
    public func decodeTopLevelObject() throws -> AnyObject?
    @warn_unused_result
    public func decodeTopLevelObjectForKey(key: String) throws -> AnyObject?
    @warn_unused_result
    public func decodeTopLevelObjectOfClass<DecodedObjectType : NSCoding where DecodedObjectType : NSObject>(cls: DecodedObjectType.Type, forKey key: String) throws -> DecodedObjectType?
    @warn_unused_result
    public func decodeTopLevelObjectOfClasses(classes: NSSet?, forKey key: String) throws -> AnyObject?
}

您可以:

do {
    try unarchiver.decodeTopLevelObjectForKey("root")
    // OR `unarchiver.decodeTopLevelObject()` depends on how you archived.
}
catch let (err) {
    print(err)
}
// -> emits something like:
// Error Domain=NSCocoaErrorDomain Code=4864 "*** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (MyProject.MyClass) for key (root); the class may be defined in source code or a library that is not linked" UserInfo={NSDebugDescription=*** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (MyProject.MyClass) for key (root); the class may be defined in source code or a library that is not linked}

这篇关于Swift只能防止NSKeyedUnarchiver.decodeObject崩溃吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆