使用 Swift 将类中的结构保存到 NSUserDefaults

Posted

技术标签:

【中文标题】使用 Swift 将类中的结构保存到 NSUserDefaults【英文标题】:Save struct in class to NSUserDefaults using Swift 【发布时间】: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 数组。两者都是我不想做的事情。

【问题讨论】:

【参考方案1】:

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

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

    final class MyStruct 
        let start : NSDate = NSDate()
        let stop : NSDate = NSDate()
    
    
    encoder.encodeObject(mystructs)
    

    将它们映射为[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)

【讨论】:

以上是关于使用 Swift 将类中的结构保存到 NSUserDefaults的主要内容,如果未能解决你的问题,请参考以下文章

如何将类中存在的 NSMutablearray 保存到文件中?

使用 NSUser Defaults 保存当前游戏分数(不是高分)?

JPA Hibernate 将类中的类中的字段映射到同一个表

Java错误排查无法将类中的构造器应用到给定类型 无法将类 uiauto.web.common.OperationNewLabelPageImpl中的构造器 OperationNewLabe

NSUser Defaults 保存按钮状态并再次显示

如何将类中的函数放入线程中? (使用 Boost 的 C++)