使用 NSKeyedUnarchiver 取消归档数组 unarchivedObject(ofClass:from:)
Unarchive Array with NSKeyedUnarchiver unarchivedObject(ofClass:from:)
自升级到 Swift 4.2 后,我发现许多 NSKeyedUnarchiver 和 NSKeyedArchiver 方法已被弃用,我们现在必须使用类型方法 static func unarchivedObject<DecodedObjectType>(ofClass: DecodedObjectType.Type, from: Data) -> DecodedObjectType?
来取消存档数据。
我已经成功归档了我定制的 class WidgetData 数组,它是一个 NSObject 子class:
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
是不允许的。我做错了什么,如何解决这个问题以取消归档我的数组?
您可以使用 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
。
unarchiveTopLevelObjectWithData(_:)
也已弃用。因此,要在没有安全编码的情况下取消归档数据,您需要:
- 用
init(forReadingFrom: Data)
创建 NSKeyedUnarchiver
- 设置
requiresSecureCoding
of created unarchiver 为 false。
- 调用
decodeObject(of: [AnyClass]?, forKey: String) -> Any?
获取您的对象,只需使用适当的 class 和 NSKeyedArchiveRootObjectKey
作为键即可。
您可能正在寻找这个:
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
}
}
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")
}
这是我更新代码的方式,它对我有用
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
}
因为 unarchiveTopLevelObjectWithData 在 iOS 14.3 之后也被弃用,只有 现在是正确的。
但是如果你不需要 NSSecureCoding 你也可以使用
使用起来非常简单,通过对 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,未归档对象的 class 实际上不会被检查并且 objective c 代码 returns 有效结果即使它被错误调用 class.
并且 swift 代码从错误的 class returns 调用时为零(因为可选的转换),但没有错误。
自升级到 Swift 4.2 后,我发现许多 NSKeyedUnarchiver 和 NSKeyedArchiver 方法已被弃用,我们现在必须使用类型方法 static func unarchivedObject<DecodedObjectType>(ofClass: DecodedObjectType.Type, from: Data) -> DecodedObjectType?
来取消存档数据。
我已经成功归档了我定制的 class WidgetData 数组,它是一个 NSObject 子class:
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
是不允许的。我做错了什么,如何解决这个问题以取消归档我的数组?
您可以使用 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
。
unarchiveTopLevelObjectWithData(_:)
也已弃用。因此,要在没有安全编码的情况下取消归档数据,您需要:
- 用
init(forReadingFrom: Data)
创建 - 设置
requiresSecureCoding
of created unarchiver 为 false。 - 调用
decodeObject(of: [AnyClass]?, forKey: String) -> Any?
获取您的对象,只需使用适当的 class 和NSKeyedArchiveRootObjectKey
作为键即可。
NSKeyedUnarchiver
您可能正在寻找这个:
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
}
}
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")
}
这是我更新代码的方式,它对我有用
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
}
因为 unarchiveTopLevelObjectWithData 在 iOS 14.3 之后也被弃用,只有
但是如果你不需要 NSSecureCoding 你也可以使用
使用起来非常简单,通过对 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,未归档对象的 class 实际上不会被检查并且 objective c 代码 returns 有效结果即使它被错误调用 class. 并且 swift 代码从错误的 class returns 调用时为零(因为可选的转换),但没有错误。