【问题标题】:Save struct in class to NSUserDefaults using Swift使用 Swift 将类中的结构保存到 NSUserDefaults
【发布时间】:2014-08-28 10:36:47
【问题描述】:

我有一个类,类内部是一个(swift)数组,基于全局结构。 我想将这个类的数组保存到 NSUserDefaults。这是我的代码:

struct mystruct {
    var start : NSDate = NSDate()
    var stop : NSDate = NSDate()
}

class MyClass : NSObject {

    var mystructs : [mystruct]

    init(mystructs : [mystruct]) {

        self.mystructs = mystructs 
        super.init()
    }

    func encodeWithCoder(encoder: NSCoder) {
        //let val = mystructs.map { $0 as NSObject } //this also doesn't work
        let objctvtmrec = NSMutableArray(mystructs)  //gives error
        encoder.encodeObject(objctvtmrec)
        //first approach:
        encoder.encodeObject(mystructs) //error: [mystructs] doesn't conform to protocol 'anyobject'
    }

}

var records : [MyClass] {
    get {
        var returnValue : [MyClass]? = NSUserDefaults.standardUserDefaults().objectForKey("records") as? [MyClass]
        if returnValue == nil
        {
            returnValue = []
        }
        return returnValue!
    }
    set (newValue) {
        let val = newValue.map { $0 as AnyObject }
        NSUserDefaults.standardUserDefaults().setObject(val, forKey: "records")
        NSUserDefaults.standardUserDefaults().synchronize()
    }
}

我已经继承了 NSObject,并且我知道我需要 NSCoding。但我没有找到任何方法将结构数组转换为 NSMuteableArray 或我可以存储的类似内容。到目前为止,唯一的想法是遍历每个条目并将其直接复制到一个新数组中,或者在整个项目中使用大量或 Objective-c 代码,因此我永远不需要从 swift 数组转换为 Objective-c 数组。两者都是我不想做的事情。

【问题讨论】:

    标签: ios uiimageview swift nsuserdefaults nscoding


    【解决方案1】:

    Swift 结构不是类,因此它们不符合AnyObject 协议。你必须重新考虑你的方法。以下是一些建议:

    1. 将您的 struct 转换为 final class 以强制执行不变性

      final class MyStruct {
          let start : NSDate = NSDate()
          let stop : NSDate = NSDate()
      }
      
      encoder.encodeObject(mystructs)
      
    2. 将它们映射为[String: NSDate] 类型的数组字典

      let structDicts = mystructs.map { ["start": $0.start, "stop": $0.stop] }
      encoder.encodeObject(structDicts)
      

    【讨论】:

    • let structDicts = mystructs.map { ["start": $0.start, "stop": $0.stop] } -> 致命错误:NSArray 元素无法匹配 Swift Array 元素类型
    【解决方案2】:

    NSUserDefaults 可以处理的类型有限:NSDataNSStringNSNumberNSDateNSArrayNSDictionaryBool。因此 no 可以保存 Swift 对象或结构。其他任何内容都必须转换为 NSData 对象。

    NSUserDefaults 的工作方式与NSArchiver 不同。由于您已经将NSCoder 添加到您的课程中,您最好的选择可能是使用NSArchiver 保存和恢复到Documents 目录中的文件..

    来自 Apple NSUserDefaults 文档:

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

    【讨论】:

      【解决方案3】:

      我开发了a small library,这可能会有所帮助。您可以将它用作 NSCoding 的 Swift 结构的替代品。

      您需要为mystruct 实现Koting 协议:

      struct mystruct: Koting {
      
          var start : NSDate = NSDate()
          var stop : NSDate = NSDate()
      
          // MARK: - Koting
      
          init?(koter: Koter) {
              guard let start: NSDate = koter.dekotObject(forKey: "start"),
                    let stop: NSDate = koter.dekotObject(forKey: "stop") else {
                  return nil
              }
              self.init(start: start, stop: stop)
          }
      
          func enkot(with koter: Koter) {
              koter.enkotObject(start, forKey: "start")
              koter.enkotObject(stop, forKey: "stop")
          }
      }
      

      从那时起,您可以轻松地将结构转换为Data 并返回:

      let str = mystruct(start: NSDate(/*...*/), stop: NSDate(/*...*/))
      guard let data = str.de_data else { return }  // potentially may be nil
      let restoredStr = mystruct.de_from(data: data)   // if data is irrelevant, returns `nil`
      

      最后,这是你为实现NSCoding所做的:

      class MyClass: NSObject, NSCoding {
      
          var mystructs: [mystruct]
      
          init(mystructs: [mystruct]) {
      
              self.mystructs = mystructs 
              super.init()
          }
      
          func encode(with aCoder: NSCoder) {
              guard let datas = mystructs.flatMap { $0.de_data } else { return }
              aCoder.encode(datas, forKey: "mystructs")
          }
      
          required convenience init?(coder aDecoder: NSCoder) {
              guard let datas = aDecoder.decodeObject(forKey: "mystructs") as? [Data],
                    let mystructs = datas.flatMap { mystruct.de_from(data: $0) } else {
                  return nil
              }
      
              self.init(mystructs : mystructs)
          }
      }
      

      如果NSCoding 支持 Swift 结构,您将编写的代码几乎相同。

      【讨论】:

        【解决方案4】:

        我在使用 Swift 4 编码时在我的项目中使用这个:

        let jsonData = """ {"variable1":1234,"variable2":"someString"}"""

        struct MyStruct:Codable{ var variable1 :Int var variable2:String }

        let whatever = try JSONDecoder().decode(MyStruct.self,from:jsonData)
        
        let encoded =try JSONEncoder().encode(whatever)
        
        UserDefaults.standart.set(encoded, forKey:"encodedData")
        

        从 UserDefaults 中获取数据

        if let data = UserDefaults.standard.object(forKey: "encodedData") as? Data {
            let myStruct = try JSONDecoder().decode(MyStruct.self, from: data)
            print(myStruct)
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-11-12
          • 1970-01-01
          • 1970-01-01
          • 2015-06-15
          • 1970-01-01
          • 2014-10-05
          • 2016-01-05
          • 2019-01-12
          相关资源
          最近更新 更多