24

自从升级到 Swift 4.2 后,我发现许多 NSKeyedUnarchiver 和 NSKeyedArchiver 方法已被弃用,我们现在必须使用 type 方法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我做错了什么,如何解决这个问题以取消归档我的阵列?

4

6 回答 6

39

您可以使用unarchiveTopLevelObjectWithData(_:)取消归档由 归档的数据archivedData(withRootObject:requiringSecureCoding:)。(我相信这还没有被弃用。)

但在显示一些代码之前,你应该更好:

  • 避免使用NSDataData改为使用

  • 避免使用try?which dispose 对调试有用的错误信息

  • 删除所有不需要的演员表


试试这个:

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.

于 2018-07-23T22:40:31.167 回答
19
unarchiveTopLevelObjectWithData(_:) 

也被弃用了。因此,要在没有安全编码的情况下取消归档数据,您需要:

  1. 创建NSKeyedUnarchiverinit(forReadingFrom: Data)
  2. requiresSecureCoding创建的 unarchiver 设置为 false。
  3. 调用decodeObject(of: [AnyClass]?, forKey: String) -> Any?以获取您的对象,只需使用正确的类并NSKeyedArchiveRootObjectKey作为键。
于 2018-09-20T22:34:01.230 回答
8

由于unarchiveTopLevelObjectWithData在 iOS 14.3 之后也被弃用,现在只有Hopreeeenjust 的答案是正确的。

但是如果你不需要 NSSecureCoding 你也可以使用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(因为可选转换),但没有错误。

于 2020-11-29T19:17:04.093 回答
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")
        }

这就是我更新代码及其为我工作的方式

于 2018-10-23T09:35:43.003 回答
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
        }
    }
于 2018-10-11T11:26:33.317 回答
3

斯威夫特 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
}
于 2020-02-05T13:39:25.977 回答