使用 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&lt;WidgetData&gt;.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) 创建NSKeyedUnarchiverrequiresSecureCoding of created unarchiver 设置为 false。 调用decodeObject(of: [AnyClass]?, forKey: String) -&gt; 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

使用 NSKeyedUnarchiver 解码 URL 对象数组

NSKeyedUnarchiver 数据格式错误

iPhone - NSKeyedUnarchiver 内存泄漏

NSKeyedUnarchiver - 删除解码的数据?

NSKeyedUnarchiver - 需要尝试/捕获?