使用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议:
-
将你的
struct
转换成final class
来执行不变性final class MyStruct { let start : NSDate = NSDate() let stop : NSDate = NSDate() } encoder.encodeObject(mystructs)
-
将它们映射为types为
[String: NSDate]
的数组字典let structDicts = mystructs.map { ["start": $0.start, "stop": $0.stop] } encoder.encodeObject(structDicts)
NSUserDefaults
在它可以处理的types中是有限的: NSData
, NSString
, NSNumber
, NSDate
, NSArray
, NSDictionary
和Bool
。 因此不能保存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结构,你会写。