一尘不染

Swift唯一的方法来防止NSKeyedUnarchiver.decodeObject崩溃?

swift

NSKeyedUnarchiver.decodeObject``SIGABRT如果原始类未知,将导致崩溃。我已经看到再追从斯威夫特的早期历史这个问题的日期,使用Objective
C语言所需的唯一解决方案(也预先日斯威夫特2的实现guardthrowstrycatch)。我可以弄清楚Objective C的路线-
但如果可能的话,我宁愿理解仅Swift的解决方案。

例如-
数据已使用编码NSPropertyListFormat.XMLFormat_v1_0unarchiver.decodeObject()如果编码数据的类别未知,则以下代码将失败。

//...
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
}
//...

我正在寻找一种Swift
2唯一的方法来尝试尝试之前测试数据是否有效.decodeObject-因为.decodeObject没有throws-这意味着try-
catch在Swift中似乎不是一个选择(没有throws包装AFAIK的方法)。否则,解码数据的另一种方法将在解码失败时捕获错误,我可以捕获该错误。我希望用户能够从iCloud驱动器或Dropbox导入文件-
因此需要对其进行正确验证。我不能认为编码后的数据是安全的。

NSKeyedUnarchiver方法.unarchiveTopLevelObjectWithData.validateValue双方都有throws。也许有某种方法可以使用它们?我无法弄清楚如何开始尝试validateValue在这种情况下实施。这甚至是一条可行的路线吗?还是我应该寻找其他方法之一来解决?

还是有人知道替代Swift
2的唯一方法来解决此问题?我相信我感兴趣的密钥可能是标题$classname,但是TBH在尝试制定实施方法上validateValue还是不够深入-
甚至这是否是坚持不懈的正确方法。我感觉到我缺少明显的东西。


编辑:这是一个解决方案-多亏了林太郎的出色回答

最初的答案为我解决了这个问题-即实现一个委托。

但是,到目前为止,我采用了围绕rintaro的其他已编辑响应构建的解决方案,如下所示:

//...
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
}
//...

阅读 878

收藏
2020-07-07

共1个答案

一尘不染

NSKeyedUnarchiver遇到未知类时,
unarchiver(_:cannotDecodeObjectOfClassName:originalClasses:)将调用委托方法。

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

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

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.

新增

我没有注意到它NSCoder具有extension更快的方法:

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}
2020-07-07