NSKeyedUnarchiver.decodeObject``SIGABRT如果原始类未知,将导致崩溃。我已经看到再追从斯威夫特的早期历史这个问题的日期,使用Objective C语言所需的唯一解决方案(也预先日斯威夫特2的实现guard,throws,try和catch)。我可以弄清楚Objective C的路线- 但如果可能的话,我宁愿理解仅Swift的解决方案。
NSKeyedUnarchiver.decodeObject``SIGABRT
guard
throws
try
catch
例如- 数据已使用编码NSPropertyListFormat.XMLFormat_v1_0。unarchiver.decodeObject()如果编码数据的类别未知,则以下代码将失败。
NSPropertyListFormat.XMLFormat_v1_0
unarchiver.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导入文件- 因此需要对其进行正确验证。我不能认为编码后的数据是安全的。
.decodeObject
该NSKeyedUnarchiver方法.unarchiveTopLevelObjectWithData与.validateValue双方都有throws。也许有某种方法可以使用它们?我无法弄清楚如何开始尝试validateValue在这种情况下实施。这甚至是一条可行的路线吗?还是我应该寻找其他方法之一来解决?
NSKeyedUnarchiver
.unarchiveTopLevelObjectWithData
.validateValue
validateValue
还是有人知道替代Swift 2的唯一方法来解决此问题?我相信我感兴趣的密钥可能是标题$classname,但是TBH在尝试制定实施方法上validateValue还是不够深入- 甚至这是否是坚持不懈的正确方法。我感觉到我缺少明显的东西。
$classname
编辑:这是一个解决方案-多亏了林太郎的出色回答
最初的答案为我解决了这个问题-即实现一个委托。
但是,到目前为止,我采用了围绕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:)将调用委托方法。
unarchiver(_:cannotDecodeObjectOfClassName:originalClasses:)
委托可以例如加载一些代码以将类引入运行时并返回该类,或者 替换为其他类对象 。如果委托返回nil,则取消归档将中止,并且该方法将引发一个NSInvalidUnarchiveOperationException。
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更快的方法:
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}