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

我有一个类,里面的类是(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() } } 

我已经subclassed NSObject,我知道我需要NSCoding。 但我没有find任何方法将结构数组转换为NSMuteableArray或类似的东西,我可以存储。 直到现在唯一的想法是通过每个条目,并将其直接复制到一个新的数组或在整个项目中使用大量或客观的C代码,所以我从来不需要从swift数组转换到Objective-C数组。 这两件事我都不想做。

Swift结构不是类 ,因此它们不符合AnyObject协议。 你必须重新思考你的方法。 这里有一些build议:

  1. 将你的struct转换成final class来执行不变性

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

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

NSUserDefaults在它可以处理的types中是有限的: NSDataNSStringNSNumberNSDateNSArrayNSDictionaryBool 。 因此不能保存Swift对象或结构体。 其他任何东西都必须转换成一个NSData对象。

NSUserDefaults不能像NSArchiver一样工作。 由于您已经将NSCoder添加到您的类中,所以最好的select可能是使用NSArchiver保存并恢复到Documents目录中的Documents

从Apple NSUserDefaults文档:

一个默认对象必须是一个属性列表,也就是一个实例(或集合的实例组合):NSData,NSString,NSNumber,NSDate,NSArray或NSDictionary。 如果要存储任何其他types的对象,则通常应将其存档以创buildNSData的实例。

我开发了一个小型图书馆 ,可能会有所帮助。 你可以使用它作为替代Swift结构的NSCoding

你需要为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结构,你会写。