从 NSUserDefaults 检索自定义对象

Posted

技术标签:

【中文标题】从 NSUserDefaults 检索自定义对象【英文标题】:Retrieving custom Object from NSUserDefaults 【发布时间】:2019-09-17 22:42:45 【问题描述】:

我有一本价值观字典

class Objects 
    let values = [
    "AAA": ["AAAAAAA", "111111111"],
    "BBB": ["BBBBBBBB", "2222222"],
    "CCC": ["CCCCCCCC", "3333333333"],
    "DDD": ["DDDDDD", "44444444"],
    ]

我将其转换为自定义对象并显示在表格视图中。

struct  Object 
   var heading : String!
   var imageName: String!

然后用户可以选择两个对象存储在UserDefaults

let defaults = UserDefaults.standard

func addObject(_ object1: String, object2: String) 

    // Get objects for user
    var userObjects = fetchObjectsFromUserDefaults()

    // Add to user currencies
    userObjects.append([object1,object2])

    //Update user defaults value for key
    // [ [Object1, Object2], [Object1, Object2] ]

    defaults.set(userObject, forKey: "userCurrencies")


// Gets [[String]] values from user defaults for key
func fetchObjectsFromUserDefaults() -> [[String]] 
    if let objects = UserDefaults.standard.value(forKey: "userObjects") 
        return objects as! [[String]]
     else 
        return []
    



// Uses [[String]] values and turns them into objects by using the dictionary to determine property values 

func getObject() -> [[Object]] 
    let userObject = fetchObjectsFromUserDefaults()
    // [ [Object1, Object2], [Object1, Object2] ]

    let object = Object()

    var fetchedObject = [[Object]]()

    if !userObjects.isEmpty 
        for c in userObjects 
            var set = [Object]()
            if let val = object.available[c[0]] 
                set.append(Currency(currencyTitle: c[0], imageName: val[0] ))
            

            if let val2 = object.available[c[1]] 
                set.append(Currency(currencyTitle: c[0], imageName: val2[0] ))
            

            if !set.isEmpty 
                fetchedObjects.append(set)
            
        
        return fetchedObjects
    

    return [[]]


视图控制器

在这里我将对象加载到TableView

let fetched = dataManager.getObjects
print(fetched)
self.objects = fetched()

但是这会打印出来

(功能)

我做错了什么,他们是从用户默认值中存储和检索这些数据的更好方法吗?我觉得这已经结束了,还有一种更快捷、更安全的方法。

【问题讨论】:

您只调用该函数一次。看起来您至少可能想在 print 语句中调用它(print(fetched()) 以打印结果。或者打印语句甚至是您要解决的问题? 您需要使您的自定义对象符合 NSCoding 并且您需要对其进行编码/解码并将其作为数据保存到 UserDefaults。顺便说一句,不要使用 value(forKey:) 方法,它仅适用于 KVO,如果您使用的是 Swift 4 或更高版本,最好使用 Codable 协议来编码/解码您的自定义对象。 另外要提的是结构不能符合 NSCoding(你需要继承 NSObject)。使其符合 Codable 协议并将编码数据保存到 plist 文件中 【参考方案1】:

第 1 步。

制作你的结构Codable。如果您的struct 的所有成员都是Codable 并且幸运的是StringCodable,那么编译器将为您编写所有函数,所以它只是:

struct Object: Codable 
  var heading : String!
  var imageName: String!

第 2 步。

Codable 的问题在于它与Data 相互转换,但您想与Dictionary 相互转换。幸运的是 JSONSerialization 从 Data 转换为 Dictionary 所以创建一个新协议并给它一个带有协议扩展的默认实现:

protocol JSONRepresentable 
  init?(json: [String: Any])
  func json() -> [String: Any]

extension JSONRepresentable where Self: Codable 
  init?(json: [String:Any]) 
    guard let value = (try? JSONSerialization.data(withJSONObject: json, options: []))
      .flatMap ( try? JSONDecoder().decode(Self.self, from: $0) ) else 
        return nil
    
    self = value
  
  func json() -> [String:Any] 
    return (try? JSONEncoder().encode(self))
      .flatMap  try? JSONSerialization.jsonObject(with: $0, options: [])  as? [String: Any] ?? [:]
  

第 3 步。

使你的结构符合JSONRepresentable

struct Object: Codable, JSONRepresentable 
  var heading : String!
  var imageName: String!

第 4 步。

将您的对象放入 Userdefaults 并再次取出:

let o = Object.init(heading: "s", imageName: "a").json()
UserDefaults.standard.set(o, forKey: "test")
print(Object.init(json: UserDefaults.standard.dictionary(forKey: "test") ?? [:]))

如果你想试试,这里是整个游乐场:

import UIKit

struct Object: Codable, JSONRepresentable 
  var heading : String!
  var imageName: String!


protocol JSONRepresentable 
  init?(json: [String: Any])
  func json() -> [String: Any]

extension JSONRepresentable where Self: Codable 
  init?(json: [String:Any]) 
    guard let value = (try? JSONSerialization.data(withJSONObject: json, options: []))
      .flatMap ( try? JSONDecoder().decode(Self.self, from: $0) ) else 
        return nil
    
    self = value
  
  func json() -> [String:Any] 
    return (try? JSONEncoder().encode(self))
      .flatMap  try? JSONSerialization.jsonObject(with: $0, options: [])  as? [String: Any] ?? [:]
  


let o = Object.init(heading: "s", imageName: "a").json()
UserDefaults.standard.set(o, forKey: "test")
print(Object.init(json: UserDefaults.standard.dictionary(forKey: "test") ?? [:]))

【讨论】:

谢谢!这是很大的帮助,只是试图让它完全适用于我的项目,例如添加对象时,我在 print 语句中得到以下内容 Optional(TestApp.Currency(heading: nil, imageName: nil)) 我还需要添加一对对象,因此在保存和检索它们时,对象的格式是一个包含两个对象的数组中的数组(我比较每个表格视图单元格中的每个对象)[[对象,对象]] 只是弄清楚这些最后的部分并做一些进一步的阅读,但这更多是我想要的 Array 也是可编码的。所以它的工作方式相同。

以上是关于从 NSUserDefaults 检索自定义对象的主要内容,如果未能解决你的问题,请参考以下文章

NSUserDefaults 简介,使用 NSUserDefaults 存储自定义对象

为啥 `NSUserDefaults` 不允许存储自定义类的对象?

NSUserDefaults存储自定义对象

NSUserDefaults读取和写入自定义对象

如何在 Swift 中归档和取消归档自定义对象?或者如何在 Swift 中将自定义对象保存到 NSUserDefaults?

将自定义对象存储到 NSMutableArray,然后将此 NSMutableArray 存储到 NSUserDefaults