【问题标题】:Unarchive Array with NSKeyedUnarchiver unarchivedObject(ofClass:from:)使用 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。我做错了什么,如何解决这个问题以取消归档我的数组?

【问题讨论】:

  • 检查这个以获得更快速的解决方案stackoverflow.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! [频道收藏]

标签: ios swift nskeyedunarchiver ios12


【解决方案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(_:) 

也已弃用。因此,要在没有安全编码的情况下解压缩数据,您需要:

  1. init(forReadingFrom: Data) 创建NSKeyedUnarchiver
  2. requiresSecureCoding of created unarchiver 设置为 false。
  3. 调用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(因为可选转换),但没有错误。

      【讨论】:

      • 我无法强调我对这个答案的存在感到多么欣慰。鉴于接受的答案暗示了一些也将被弃用的东西,拥有一个有望无限期工作的答案非常有用。
      • 这是最好的
      猜你喜欢
      • 1970-01-01
      • 2019-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-21
      • 1970-01-01
      相关资源
      最近更新 更多