使用 NSKeyedUnarchiver unarchivedObject(ofClass:from:) 取消归档数组
Posted
技术标签:
【中文标题】使用 NSKeyedUnarchiver unarchivedObject(ofClass:from:) 取消归档数组【英文标题】:Unarchive Array with NSKeyedUnarchiver unarchivedObject(ofClass:from:) 【发布时间】:2018-12-31 10:14:40 【问题描述】:自从升级到 Swift 4.2 后,我发现许多 NSKeyedUnarchiver 和 NSKeyedArchiver 方法已被弃用,我们现在必须使用类型方法 static func unarchivedObject<DecodedObjectType>(ofClass: DecodedObjectType.Type, from: Data) -> DecodedObjectType?
来取消归档数据。
我已经成功地归档了我的定制类 WidgetData 的一个数组,它是一个 NSObject 子类:
private static func archiveWidgetDataArray(widgetDataArray : [WidgetData]) -> NSData
guard let data = try? NSKeyedArchiver.archivedData(withRootObject: widgetDataArray as Array, requiringSecureCoding: false) as NSData
else fatalError("Can't encode data")
return data
当我尝试取消归档这些数据时,问题就来了:
static func loadWidgetDataArray() -> [WidgetData]?
if isKeyPresentInUserDefaults(key: USER_DEFAULTS_KEY_WIDGET_DATA)
if let unarchivedObject = UserDefaults.standard.object(forKey: USER_DEFAULTS_KEY_WIDGET_DATA) as? Data
//THIS FUNCTION HAS NOW BEEN DEPRECATED:
//return NSKeyedUnarchiver.unarchiveObject(with: unarchivedObject as Data) as? [WidgetData]
guard let nsArray = try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSArray.self, from: unarchivedObject as Data) else
fatalError("loadWidgetDataArray - Can't encode data")
guard let array = nsArray as? Array<WidgetData> else
fatalError("loadWidgetDataArray - Can't get Array")
return array
return nil
但这失败了,因为不允许使用Array.self
而不是NSArray.self
。我做错了什么,如何解决这个问题以取消归档我的数组?
【问题讨论】:
检查这个以获得更快速的解决方案***.com/a/51460950/5820010 什么“失败”?你得到什么输出?你没有在任何地方使用Array.self
。
当更改为 Array.self 而不是 NSArray.self 时,预编译器会报错: Incorrect argument label in call (have 'ofClass:from:', expected 'ofClasses:from:') Replace ' ofClass' 与 'ofClasses'。这意味着 Array.self 不能使用。当我使用 NSArray.self 时,它可以毫无问题地编译并运行。但是被致命错误捕获,因为结果 'nsArray' 为 nil。
那么,当您使用NSKeyedUnarchiver.unarchivedObject(ofClasses: [Array<WidgetData>.self], from: unarchivedObject as Data)
时会发生什么?另外,不要使用try?
,而是使用do/try/catch
,这样您就可以打印发生的实际错误。
守卫让 unarchivedFavorites = 试试? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(favoritesData!) else return self.channelFavorites = unarchivedFavorites as! [频道收藏]
【参考方案1】:
您可以使用unarchiveTopLevelObjectWithData(_:)
取消归档archivedData(withRootObject:requiringSecureCoding:)
归档的数据。 (我相信这还没有被弃用。)
但是在展示一些代码之前,你最好:
避免使用NSData
,改用Data
避免使用try?
,它会释放对调试有用的错误信息
删除所有不需要的演员表
试试这个:
private static func archiveWidgetDataArray(widgetDataArray : [WidgetData]) -> Data
do
let data = try NSKeyedArchiver.archivedData(withRootObject: widgetDataArray, requiringSecureCoding: false)
return data
catch
fatalError("Can't encode data: \(error)")
static func loadWidgetDataArray() -> [WidgetData]?
guard
isKeyPresentInUserDefaults(key: USER_DEFAULTS_KEY_WIDGET_DATA), //<- Do you really need this line?
let unarchivedObject = UserDefaults.standard.data(forKey: USER_DEFAULTS_KEY_WIDGET_DATA)
else
return nil
do
guard let array = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(unarchivedObject) as? [WidgetData] else
fatalError("loadWidgetDataArray - Can't get Array")
return array
catch
fatalError("loadWidgetDataArray - Can't encode data: \(error)")
但如果你正在制作一个新应用,你最好考虑使用Codable
。
【讨论】:
这句话一针见血。太感谢了。是的,我正在制作一个新应用程序。如果你能对 Codable 有所了解。你引起了我的注意。我应该从哪里开始?为什么它很重要。 @GeoffH,它是原生的 Swift 序列化(归档)方式。 (例如,它适用于[WidgetData].self
,您无需关心NSArray
或其他NS
-things。)最好从搜索“swift codable”开始,您可以找到很多好文章。
会的。非常感谢您的帮助。
完美答案。非常感谢。【参考方案2】:
unarchiveTopLevelObjectWithData(_:)
也已弃用。因此,要在没有安全编码的情况下解压缩数据,您需要:
-
用
init(forReadingFrom: Data)
创建NSKeyedUnarchiver
将 requiresSecureCoding
of created unarchiver 设置为 false。
调用decodeObject(of: [AnyClass]?, forKey: String) -> Any?
获取您的对象,只需使用适当的类和NSKeyedArchiveRootObjectKey
作为键。
【讨论】:
【参考方案3】:您可能正在寻找这个:
if let widgetsData = UserDefaults.standard.data(forKey: USER_DEFAULTS_KEY_WIDGET_DATA)
if let widgets = (try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, WidgetData.self], from: widgetsData)) as? [WidgetData]
// your code
【讨论】:
我可以使用“[NSArray.self, WidgetData.self]”解决与您的方法相同的问题。但是,我找不到有关您的方法的公开文件。你知道文档网址吗?【参考方案4】: if #available(ios 12.0, *)
guard let unarchivedFavorites = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(favoritesData!)
else
return
self.channelFavorites = unarchivedFavorites as! [ChannelFavorite]
else
if let unarchivedFavorites = NSKeyedUnarchiver.unarchiveObject(with: favoritesData!) as? [ChannelFavorite]
self.channelFavorites = unarchivedFavorites
//获取数据
if #available(iOS 12.0, *)
// use iOS 12-only feature
do
let data = try NSKeyedArchiver.archivedData(withRootObject: channelFavorites, requiringSecureCoding: false)
UserDefaults.standard.set(data, forKey: "channelFavorites")
catch
return
else
// handle older versions
let data = NSKeyedArchiver.archivedData(withRootObject: channelFavorites)
UserDefaults.standard.set(data, forKey: "channelFavorites")
这就是我更新代码及其为我工作的方式
【讨论】:
您的可用性宏没有意义。在第一个代码块中,为什么会出现?unarchiveTopLevelObjectWithData()
在 iOS 9 中引入,在 iOS 12 中弃用。【参考方案5】:
Swift 5-IOS 13
guard let mainData = UserDefaults.standard.object(forKey: "eventDetail") as? NSData
else
print(" data not found in UserDefaults")
return
do
guard let finalArray =
try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(mainData as Data) as? [EventDetail]
else
return
self.eventDetail = finalArray
【讨论】:
【参考方案6】:由于 unarchiveTopLevelObjectWithData 在 iOS 14.3 之后也被弃用,现在只有 Hopreeeenjust's answer 是正确的。
但是如果你不需要 NSSecureCoding 你也可以使用answer of Maciej S
非常容易使用,通过添加 NSCoding 协议的扩展:
extension NSCoding where Self: NSObject
static func unsecureUnarchived(from data: Data) -> Self?
do
let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data)
unarchiver.requiresSecureCoding = false
let obj = unarchiver.decodeObject(of: self, forKey: NSKeyedArchiveRootObjectKey)
if let error = unarchiver.error
print("Error:\(error)")
return obj
catch
print("Error:\(error)")
return nil
使用此扩展名取消归档,例如NSArray 你只需要:
let myArray = NSArray.unsecureUnarchived(from: data)
对于 Objective C,使用 NSObject 类别:
+ (instancetype)unsecureUnarchivedFromData:(NSData *)data
NSError * err = nil;
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData: data error: &err];
unarchiver.requiresSecureCoding = NO;
id res = [unarchiver decodeObjectOfClass:self forKey:NSKeyedArchiveRootObjectKey];
err = err ?: unarchiver.error;
if (err != nil)
NSLog(@"NSKeyedUnarchiver unarchivedObject error: %@", err);
return res;
请注意,如果 requiresSecureCoding 为 false,则未归档对象的类实际上不会被检查,并且即使从错误的类调用目标 c 代码也会返回有效结果。 从错误的类调用 swift 代码时返回 nil(因为可选转换),但没有错误。
【讨论】:
我无法强调我对这个答案的存在感到多么欣慰。鉴于接受的答案暗示了一些也将被弃用的东西,拥有一个有望无限期工作的答案非常有用。 这是最好的以上是关于使用 NSKeyedUnarchiver unarchivedObject(ofClass:from:) 取消归档数组的主要内容,如果未能解决你的问题,请参考以下文章
使用 NSKeyedUnarchiver 解码 URL 对象数组