【问题标题】:NSUserDefaultsController: "Attempt to set a non-property-list object ... as an NSUserDefaults/CFPreferences value for key ..."NSUserDefaultsController:“尝试将非属性列表对象...设置为键的 NSUserDefaults/CFPreferences 值...”
【发布时间】:2019-06-19 21:55:09
【问题描述】:

我正在尝试使用 NSUserDefaultsController 在我的 Swift macOS 应用程序中实现首选项窗口。

我需要保留的设置之一是一组预设,由以下类定义:

class Preset: NSObject {
    var name = "abc"
    var value = 123

    override init() {
        super.init()
    }
}

因此,我需要持久化一个[Presets] 类型的变量。我的首选项窗口中的可视化表示是NSTableView,绑定到NSArrayController

我按照this 教程设置绑定。运行应用程序并尝试添加新预设时(通过单击 + 按钮),我收到以下错误:

[User Defaults] Attempt to set a non-property-list object (...) as an NSUserDefaults/CFPreferences value for key presets
[General] Attempt to insert non-property list object (...) for key presets

我已尝试实现CodableNSCoding 协议,但错误仍然存​​在。

通过在 Stack Overflow 中搜索类似问题(例如 thisthis),似乎解决方案将涉及 NSKeyedArchiverNSKeyedUnarchiver。如果您手动触发加载和保存,这看起来很简单。不幸的是,我看不到如何将这些类与NSUserDefaultsController 一起使用。

【问题讨论】:

  • 如果你想使用NSUserDefaultsController和Cocoa Bindings你必须实现一个ValueTransformer来序列化自定义对象。我推荐使用Codable,那么一个简单的struct就足够了。
  • @vadian 我是 Cocoa/Swift 新手。你能详细说明一下吗?你的意思是我需要实现一个继承自ValueTransformer 并实现Codable 协议的类?也许你可以把它变成一个答案——看起来这会解决我的问题,我可以接受。
  • 不,数据模型是采用Codable的普通结构或类。假设有一个数组@objc dynamic var presets = [Preset](),你必须实现ValueTransformer 的一个子类来解码和编码从DataData 的数组。然后您可以将presets 绑定到NSUserDefaultsController 应用值转换器。
  • 好的,我知道我应该在这里表​​现出一点努力,但我是一个完全的新手,我迷路了。所以我需要创建一个具有单个属性@objc dynamic var presets = [Preset]() 的类?如果您能一步一步地回答,我将非常感激。

标签: swift cocoa nsuserdefaults codable nscoding


【解决方案1】:

我通过子类化NSArrayController 解决了这个问题,如下所示(请参阅 Hamish 对my other question 的评论,这是使这个通用化拼图的最后一个缺失部分):

extension Encodable {
    fileprivate func encode(to container: inout SingleValueEncodingContainer) throws {
        try container.encode(self)
    }
}

struct AnyEncodable: Encodable {
    var value: Encodable
    init(_ value: Encodable) {
        self.value = value
    }
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try value.encode(to: &container)
    }
}

class NSEncodableArrayController: NSArrayController {
    override func addObject(_ object: Any) {
        let data = try! PropertyListEncoder().encode(AnyEncodable(object as! Encodable))
        let any = try! PropertyListSerialization.propertyList(from: data, options: [], format: nil)

        super.addObject(any)
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-12
    • 1970-01-01
    • 1970-01-01
    • 2019-08-23
    • 2014-06-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多