当前位置: 首页 > 面试题库 >

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

王亮
2023-03-14
问题内容

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

问题答案:

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}


 类似资料:
  • 我有一个discord bot,我正在处理一个命令,该命令从mysql数据库中显示关于一个字符的信息(通过提供字符名)。它工作得很好,但如果我提供了一个数据库中不存在的名称,如,则bot会崩溃。所以我的问题是。希望你能理解我的意思。 代码如下: 如果你需要我提供你更多的信息,让我知道。 感谢任何帮助!

  • 本文向大家介绍Swift用final关键字来防止重写,包括了Swift用final关键字来防止重写的使用技巧和注意事项,需要的朋友参考一下 final关键字在大多数的编程语言中都存在,表示不允许对其修饰的内容进行继承或者重新操作。Swift中,final关键字可以在class、func和var前修饰。 通常大家都认为使用final可以更好地对代码进行版本控制,发挥更佳的性能,同时使代码更安全。下面

  • 在本节中,我们将学习如何保护交付方法。使用XArp或静态ARP表等工具来防止中间人攻击,避免不知道的网络。另一个预防措施是确保在下载更新时使用HTTP。这将降低下载虚假更新的风险。 我们将学习另一个有用的工具,即WinMD5。当程序的签名或校验和以任何方式被修改时,该程序将提醒我们,这表明该文件不是原始文件。为了检查,我们将下载并运行WinMD5,我们可以在其中比较文件的签名和校验和。如果签名和校

  • 是否有一种方法可以防止重写我在映射器类中为其提供了实现的特定方法? null 我的映射器类如下所示: 然后,Mapstruct生成如下所示的实现:

  • 问题内容: 我一直在开发一个Android应用程序,该应用程序经常使用以防止它崩溃,即使在不需要的地方也是如此。例如, 在视图与被引用,如: 在整个应用程序中都使用这种方法。堆栈跟踪没有打印出来,很难找到问题所在。该应用程序突然关闭而不打印任何堆栈跟踪。 我请我的长辈向我解释一下,他说: 这是为了防止生产崩溃。 我完全不同意 。对我来说,这不是防止应用程序崩溃的方法。它表明开发人员 不 知道自己在