【问题标题】:Why is Set not a property list type?为什么 Set 不是属性列表类型?
【发布时间】:2019-02-21 07:19:16
【问题描述】:

我试图在用户默认值中保存 Set<String> 类型并得到一个 “尝试设置非属性列表对象”错误..

这让我想知道,为什么 Set 不是属性列表类型?

为什么允许使用数组和字典,但不允许使用集合?

编辑:我知道存储数据的其他方式。我只是想知道 Apple 决定 Sets 不是属性列表类型是否有已知或估计的原因。

是不是因为 Set 没有像 Arrays (index) 和 Dictionaries (key) 这样的键?是因为集合是无序的吗?但是字典也是无序的..

【问题讨论】:

  • 你期待什么答案? Apple 没有在属性列表中实现集合。在developer.apple.com/documentation/foundation/userdefaults中记录了用户默认可以存储什么样的对象。
  • 我想知道为什么 Apple 没有在属性列表中实现集合。是因为他们没有钥匙吗?这会让哈希变得困难吗?
  • 我不确定是否可以回答“为什么”。属性列表类似于 XML 文件,Apple 可以实现了一个<set>...</set> 元素,类似于<array>...</array>。也许它有一些历史原因,也许只是没有必要。 – 所有这些东西都比 Swift 早了很多年,也许 NSSet 是在属性列表之后引入的?我不知道。
  • 我明白了。我想在苹果告诉我们之前我们不会知道。谢谢!
  • 编码setsarrays在实践上没有区别。编码时总是可以将集合转换为数组,解码后再次转换为集合。

标签: ios swift nsuserdefaults swift4.2


【解决方案1】:

属性列表代表数据,而不是实现细节。数组是有序列表。 dict 是字符串键到值的映射。什么是“集合”?在简单的文本数据中,这并不意味着任何特别之处。如果列表不能有重复项,请不要写重复项。如果它是无序的,则忽略该顺序。一切“类集合”都与数据操作有关。 (对于实际上确实具有重复元素的 plist-set,您会期望什么行为?它会无效吗?如果不是,它怎么不仅仅是一个数组?)

属性列表不是通用的对象树序列化工具。这就是 NSCoder 的用途。它们主要是为像 Info.plist 这样的人类可写配置而设计的,并且可以用任何语言进行解析。 NSUserDefaults 也不是通用数据存储(如果是,它会被称为“NSApplicationDataStore”或类似的东西)。它旨在以用户可以手动编辑的方式存储用户配置数据。

原始 (NeXTSTEP) 格式的输入要简单得多:

SomeKey = {
    MyFirstProperty = ("here's", "a", "list");
    SomeOtherThing = "I'm a string";
};

Apple 在 OS X 中切换到 XML,使得输入更加困难,但基本方法是相同的。 (在 OS X 的早期,Apple 采取了很多举措将 Java 集成到系统中,包括更多地依赖 XML 包括 formal DTDs。回想起来,其中许多举措可能是不幸的,但它们在当时是有道理的.)

Plist 解决了与 JSON 相同的问题。它们提供了编码原始数据所需的最小类型集,而不是用于实现算法的数据结构。

我并不是说 plist 是根据一些数学第一原理创建的。如果 NSSet 是配置数据中非常常见的类型,我敢肯定它会在格式中找到它的方式。但它们并不是被发明为保存任意应用程序数据的通用方法,NSUserDefaults 也不是。

【讨论】:

    【解决方案2】:

    您需要先存档您的集合以使其成为Data 对象。只有这样,您才能将该数据存储为默认值。

    像这样:

    let sets: Set<String> = Set(arrayLiteral: "a","b","c")
    let encodedData = NSKeyedArchiver.archivedData(withRootObject: sets)
    UserDefaults.standard.setValue(encodedData, forKeyPath: "test")
    

    然后要取消归档您的数据,这样做(然后您可以取回您的数据集)

    if let data = UserDefaults.standard.data(forKey: "test"),
        let set = NSKeyedUnarchiver.unarchiveObject(with: data) as? Set<String> {
        print(set)
    }
    

    至于不存档就无法存储设备的原因,Apple 是这样说的:

    默认对象必须是一个属性列表——即(或 对于集合,NSData、NSString 的实例的组合, NSNumber、NSDate、NSArray 或 NSDictionary。如果你想存储任何 其他类型的对象,您通常应该将其归档以创建一个 NSData 的实例。

    https://developer.apple.com/documentation/foundation/userdefaults

    如您所见,如果您将集合转换为 ArrayData,则最好将其存储到 UserDefaults

    【讨论】:

    • 感谢您的回答。但我知道如何存储非财产清单数据。我只是想知道为什么 Sets 被决定为非属性列表类型。
    • 顺便说一句,'archivedData(withRootObject:)' was deprecated in iOS 12.0: Use +archivedDataWithRootObject:requiringSecureCoding:error: instead
    • 在 iOS 12.0 中已弃用。如果你支持 iOS 12 及更低版本(例如 11.x),你应该支持,你会没事的。
    猜你喜欢
    • 2012-09-14
    • 1970-01-01
    • 2012-10-21
    • 1970-01-01
    • 2018-07-31
    • 2019-07-16
    • 1970-01-01
    • 2012-02-28
    • 2010-12-06
    相关资源
    最近更新 更多