【问题标题】:Persist an array of custom structs in Swift在 Swift 中持久化一组自定义结构
【发布时间】:2016-04-06 22:25:31
【问题描述】:

我正在尝试存储一组自定义结构。

在下面的尝试中,我在 saveSampleArrayOfQuerySettings() 的第二行中遇到运行时错误,抱怨强制转换为 AnyObject。

struct QuerySettings {

    //    let ARRAY_INDEX_WHERE_TO_SAVE_STRUCT = 0

    let QUERY_SETTINGS_KEY = "querysettings"
    let defaults = NSUserDefaults.standardUserDefaults()

    private var _includeCompletedReminders: Bool = false        // Default value
    var includeCompletedReminders: Bool {
        get {
            // If value has been set before then use it otherwise retrieve it.
            return _includeCompletedReminders
        } set (newVal) {
            _includeCompletedReminders = newVal
            //            saveSettings(self, index: ARRAY_INDEX_WHERE_TO_SAVE_STRUCT, saveKey: QUERY_SETTINGS_KEY)
        }
    }

    private var _includeRemindersWithNoDueDate: Bool = true
    var includeRemindersWithNoDueDate: Bool {
        get {
            return _includeRemindersWithNoDueDate
        } set (newVal) {
            _includeRemindersWithNoDueDate = newVal
            //            saveSettings(self, index: ARRAY_INDEX_WHERE_TO_SAVE_STRUCT, saveKey: QUERY_SETTINGS_KEY)
        }
    }
}

func saveSampleArrayOfQuerySettings(saveKey: String) {
    let sampleArray = [QuerySettings(), QuerySettings()]

    // Persist
    let archivedSettingsArray = NSKeyedArchiver.archivedDataWithRootObject(sampleArray as! AnyObject)

    // RUNTIME ERROR AT PREVIOUS LINE: "Could not cast value of type 'Swift.Array<RemindersPro.QuerySettings>' (0x112df10d8) to 'Swift.AnyObject' (0x1169d6018)."

    NSUserDefaults.standardUserDefaults().setObject(archivedSettingsArray, forKey: saveKey)
    NSUserDefaults.standardUserDefaults().synchronize()

    // For Testing Purposes - Load the saved settings
    if let retrievedArchivedSettingsArray : AnyObject = NSUserDefaults.standardUserDefaults().objectForKey(saveKey) {
        if let settingsArray : AnyObject = NSKeyedUnarchiver.unarchiveObjectWithData(retrievedArchivedSettingsArray as! NSData) {
            let arr = settingsArray as! [QuerySettings]
            print(arr.first)
        }
    }
}

saveSampleArrayOfQuerySettings("saveKey")

我想知道是否需要对我的结构进行编码。我查看了这些帖子,但奇怪的是甚至无法让帖子中的示例正常工作(更不用说将它们应用到我的代码中了。)

How to save an array of objects to NSUserDefault with swift?

(Swift) Storing and retrieving Array to NSUserDefaults

How to save an array of custom structs to plist swift

你能告诉我如何让它工作吗?

【问题讨论】:

标签: arrays swift struct nsuserdefaults persist


【解决方案1】:

你的代码太冗长了:

  1. 您不需要显式定义类型。分配默认值truefalse 足以让编译器推断出您想要Bool。 Swift 的构建理念是编译器可以变得非常智能以减轻程序员的负担。
  2. 您的自定义 getter 和 setter 没有什么特别之处,因此最好让 Swift 自动合成它。

现在,关于归档的话题:你会很沮丧地得知NSCoding 仅适用于继承自NSObject 的对象。不能使结构符合NSCoding。您需要将它包装在一个辅助类中。


这是我想出的:

1。定义一个帮助类来序列化结构:

protocol ArchivableStruct {
    var dataDictionary: [String: AnyObject] { get }
    init(dataDictionary aDict: [String: AnyObject])
}

class StructArchiver<T: ArchivableStruct>: NSObject, NSCoding {
    var structValue: T

    init(structValue: T) {
        self.structValue = structValue
    }

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(self.structValue.dataDictionary, forKey: "dataDictionary")
    }

    required init?(coder aDecoder: NSCoder) {
        let dataDictionary = aDecoder.decodeObjectForKey("dataDictionary") as! [String: AnyObject]
        self.structValue = T(dataDictionary: dataDictionary)
    }
}

2。将结构中的数据导出到字典中

字典是被存档的。在回来的路上,我们简单地解压这个 Dictionary 来获取结构体。我们不用担心QUERY_SETTINGS_KEYdefaults,因为前者是常量,而后者有自己的数据源(NSUserDefaults)。

struct QuerySettings: ArchivableStruct {
    let QUERY_SETTINGS_KEY = "querysettings"
    let defaults = NSUserDefaults.standardUserDefaults()

    var includeCompletedReminders = false
    var includeRemindersWithNoDueDate = true

    init(includeCompletedReminders: Bool, includeRemindersWithNoDueDate: Bool) {
        self.includeCompletedReminders = includeCompletedReminders
        self.includeRemindersWithNoDueDate = includeRemindersWithNoDueDate
    }

    // --------------------------------------
    // ArchivableStruct protocol
    // --------------------------------------
    var dataDictionary: [String: AnyObject] {
        get {
            return [
                "includeCompletedReminders": self.includeCompletedReminders,
                "includeRemindersWithNoDueDate": self.includeRemindersWithNoDueDate
            ]
        }
    }

    init(dataDictionary aDict: [String : AnyObject]) {
        self.includeCompletedReminders = aDict["includeCompletedReminders"] as! Bool
        self.includeRemindersWithNoDueDate = aDict["includeRemindersWithNoDueDate"] as! Bool
    }
}

3。从struct 映射到助手类

由于您归档的不是 QuerySetting 结构而是它们的数组,因此需要额外的步骤将其映射到 StructArchiver&lt;T&gt; 以便可以归档您的数组:

// Set up a sample array
let a = QuerySettings(includeCompletedReminders: true, includeRemindersWithNoDueDate: false)
let b = QuerySettings(includeCompletedReminders: false, includeRemindersWithNoDueDate: true)
let sampleArray = [a, b]

// Archive
let wrapperArray = sampleArray.map { StructArchiver(structValue: $0) }
let data = NSKeyedArchiver.archivedDataWithRootObject(wrapperArray)

// Unarchive
let unwrapperArray = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! [StructArchiver<QuerySettings>]
let unarchivedArray = unwrapperArray.map { $0.structValue }

【讨论】:

  • 感谢您的详细解答! (代码看起来很冗长,因为我注释掉了 saveSettings() 调用 - 每当我更改 var 时,它都会自动持久化。)但是,我决定让我的结构成为一个类。只是似乎不那么复杂的解决方案。我对结构似乎是类的二等公民感到失望。 (没有双关语。)虽然核心语言支持它,但似乎框架确实希望你走不同的方向——类、协议等。我想遵循一种更喜欢结构的函数式编程风格。感觉就像就像逆流而上……
  • @Daniel NSCoder 非常古老,因此深深植根于 ObjC。 C 和 ObjC 中的 struct 远没有 Swift 中的强大。 Apple 有一项艰巨的任务是更新 Cocoa 库以跟上 Swift 的特性。但另一方面,NSCoder 确实支持一些结构,一些结构,例如 CGPointCGSize
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-07
  • 1970-01-01
  • 1970-01-01
  • 2011-03-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多