保存和加载自定义字典 - NSUserDefaults

4

我有一个[Int:Bool]字典,想要把它保存到NSDictionary中。但是,它会崩溃并显示错误信息尝试设置一个非属性列表对象

let dictionary = [Int:Bool]()


self.dictionary[2] = true
self.dictionary[3] = false

NSUserDefaults.standardUserDefaults().setObject(dictionary, forKey: "dictionary")

此外,对于加载,我首先尝试了这个方法,但错误日志显示它严格要求AnyObject?。
NSUserDefaults.standardUserDefaults().objectForKey("dictionary")

然后我尝试了这个,并记录了以下信息:

NSUserDefaults.standardUserDefaults().objectForKey("dictionary") as? [Int:Bool]

我也尝试了dictionaryForKey。我得到了...

NSUserDefaults.standardUserDefaults().dictionaryForKey("dictionary")

无法将类型为[String: AnyObject]的值赋给类型[Int:Bool]


那么这两种方法哪一种更好呢?(在我的情况下,这些值应该是可选的)

1.
NSUserDefaults.standardUserDefaults().objectForKey("dictionary") as? [Int:Bool] ?? [Int:Bool]()

2.
NSUserDefaults.standardUserDefaults().objectForKey("dictionary") as? [Int:Bool])!

你在保存后同步了你的NSUserDefaults吗?NSUserDefaults.standardUserDefaults().synchronize() - Dejan Skledar
如果您在当前范围内声明了“dictionary”,为什么要使用“self.”访问它? - Tim Vermeulen
此外,显然你不能向使用 let 声明的字典中添加值... - Tim Vermeulen
我想在其他页面中使用它(并在重新打开应用程序后使用)。我尝试使用:let data = NSKeyedArchiver.archivedDataWithRootObject(self.dictionary) self.defaults.setObject(data, forKey:"key") 但是在加载时无法理解该怎么做.. :/ - senty
@senty,你仍然无法修改使用let声明的字典。除非你提供的代码在同一作用域内,否则self.dictionary将无法访问刚刚声明的字典。 - Tim Vermeulen
显示剩余2条评论
4个回答

15

Swift 4

在基本类型中,UserDefaults 可以保存符合 Codable 协议的任何对象。其中,Dictionary 是实现了该协议的类型之一。你甚至不需要编写任何自定义代码:

let dictionary = ["name": "Adam"]

// Save to User Defaults
UserDefaults.standard.set(dictionary, forKey: "names") 

// Read from User Defaults     
let saved = UserDefaults.standard.value(forKey: "names") as? [String: String]

了解更多有关Codable的信息。

Swift 3

只要键和值的类型可以用 plist 格式(NSNumberData 等)表示,就可以使用 UserDefaults 保存 Dictionary。如果不是这种情况,我们可以在写入时将其他类型序列化为 Data,在读取时从 Data 反序列化。可以通过对 UserDefaults 进行简单的扩展并使用 NSKeyArchiver 来完成:

extension UserDefaults {
    /// Save dictionary on key
    open func set<Key, Value>(dictionary: [Key: Value]?, forKey key: String) {
        let data = NSKeyedArchiver.archivedData(withRootObject: dictionary as Any)
        set(data, forKey: key)
    }

    // Retrieve dictionary for key
    open func dictionary<Key, Value>(forKey key: String) -> [Key: Value]? {
        guard let data = object(forKey: key) as? Data else { return nil }
        return NSKeyedUnarchiver.unarchiveObject(with: data) as? [Key: Value]
    }
}

现在您可以调用这些方法:

let ages = ["Adam": 25]

// Save
UserDefaults.standard.set(dictionary: ages, forKey: "ages")

// Read
let saved: [String: Int]? = UserDefaults.standard.dictionary(forKey: "ages")
print(saved) // Optional(["Adam": 25])

Swift 2

保存自定义数据

func setCustomDictionary(dict: [Int: Bool]) {
    let keyedArch = NSKeyedArchiver.archivedDataWithRootObject(dict)

    NSUserDefaults.standardUserDefaults().setObject(keyedArch, forKey: "dictionary")
}

获取数据

func getDictionary() -> [Int: Bool]? {
    let data = NSUserDefaults.standardUserDefaults().objectForKey("dict")
    let object = NSKeyedUnarchiver.unarchiveObjectWithData(data as! NSData)
    return object as? [Int: Bool]
}

使用方法

var customDictionary = [Int: Bool]()
customDictionary[2] = true
customDictionary[3] = false

// Store data in NSUserDefaults
setCustomDictionary(customDictionary)

// Get data from NSUserDefaults
let userDefaultsDictionary = getDictionary()

1
类型为 [Int: Bool] 的东西不是自动转换为 NSDictionary 吗? - Tim Vermeulen
不是这样的。这是因为Swift字典是一个通用结构Dictionary<T,E>,而NSDictionary是ObjectiveC的NSObject派生类。它们在早期版本的Swift中被桥接,但现在不再如此。 - Said Sikira
你能提供一个来源来支持这个吗?在 playground 中,这段代码对我有效:let dictionary: NSDictionary = [1: "a", 2: "b"] - Tim Vermeulen
@TimVermeulen 看起来 NSDictionary 仍然与 Swift 的 Dictionary 进行了桥接。类型之间的转换不会自动发生,您可以使用 Swift 字典语法初始化 NSDictionary - Said Sikira
但这也可以工作:let dict = [1: "a", 2: "b"]; let nsDict: NSDictionary = dict - Tim Vermeulen
在保存函数中,我遇到了replacementObjectForKeyedArchiver的错误,出现在NSArchiver上。 - Aman Chawla

0
如果你需要更多的类型,你可以像这样使用泛型:

func saveUserDefaults<T>(withKey key: String, dict: AnyObject, myType: T.Type) {
    guard let dict = dict as? T else {
        print("Type mismatch")
        return
    }
    let archiver = NSKeyedArchiver.archivedData(withRootObject: dict)
    UserDefaults.standard.set(archiver, forKey: key)
}

func getUserDefaults<T>(withKey key: String, myType: T.Type) -> T? {
    let unarchivedObject = getUserDefaultData(withKey: key)
    return unarchivedObject as? T
}

func getUserDefaultData(withKey key: String) -> Any? {
    guard let data = UserDefaults.standard.object(forKey: key) else {
        return nil
    }
    guard let retrievedData = data as? Data else {
        return nil
    }
    return NSKeyedUnarchiver.unarchiveObject(with: retrievedData)
}

例如[Int:Int]类型的使用:
    var customDictionary = [Int: Int]()
    customDictionary[234] = 1
    customDictionary[24] = 2
    customDictionary[345] = 3

    saveUserDefaults(withKey: "hello", dict: customDictionary as AnyObject, myType: [Int: Int].self)
    let savedDictionary = getUserDefaults(withKey: "hello", myType: [Int: Int].self)
    print(savedDictionary)

0

这是针对Swift 3的

func setCustomDictionary(dict: [Int: Bool]) {
    let keyedArch = NSKeyedArchiver.archivedData(withRootObject: dict)

    UserDefaults.standard.set(keyedArch, forKey: "dictionary")
}

func getDictionary() -> [Int: Bool]? {
    let data = UserDefaults.standard.object(forKey: "dict")
    let object = NSKeyedUnarchiver.unarchiveObject(with: (data as! NSData) as Data)
    return object as? [Int: Bool]
}

0

我遇到了类似的问题,但是涉及到不同类型的数据。 我的建议是将其转换为NSData并像这样检索数据:

/// Save
NSUserDefaults.standardUserDefaults().setObject(NSKeyedArchiver.archivedDataWithRootObject(object), forKey: key)

/// Read
var data = NSUserDefaults.standardUserDefaults().objectForKey(key) as NSData
var object = NSKeyedUnarchiver.unarchiveObjectWithData(data) as [String: String]

(虽然提到了[String:String],但我实际上使用的是[[String:AnyObject]]并且可以工作,所以也许对你也有用!)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接