有没有比用NSCoder编码和解码所有东西更好的方式来保存自定义类到NSUserDefaults?

我当前的类有大约50行只是编码和解码variables,以便我的类是NSUserDefaults兼容。 有没有更好的方法来处理这个问题?

例:

init(coder aDecoder: NSCoder!) { lightEnabled = aDecoder.decodeBoolForKey("lightEnabled") soundEnabled = aDecoder.decodeBoolForKey("soundEnabled") vibrateEnabled = aDecoder.decodeBoolForKey("vibrateEnabled") pulseEnabled = aDecoder.decodeBoolForKey("pulseEnabled") songs = aDecoder.decodeObjectForKey("songs") as! [Song] currentSong = aDecoder.decodeIntegerForKey("currentSong") enableBackgroundSound = aDecoder.decodeBoolForKey("enableBackgroundSound") mixSound = aDecoder.decodeBoolForKey("mixSound") playSoundInBackground = aDecoder.decodeBoolForKey("playSoundInBackground") duckSounds = aDecoder.decodeBoolForKey("duckSounds") BPMBackground = NSKeyedUnarchiver.unarchiveObjectWithData(aDecoder.decodeObjectForKey("BPMBackgorund") as! NSData) as! UIColor! BPMPulseColor = NSKeyedUnarchiver.unarchiveObjectWithData(aDecoder.decodeObjectForKey("BPMPulseColor") as! NSData) as! UIColor! TempoBackGround = NSKeyedUnarchiver.unarchiveObjectWithData(aDecoder.decodeObjectForKey("TempoBackGround") as! NSData) as! UIColor! TempoPulseColor = NSKeyedUnarchiver.unarchiveObjectWithData(aDecoder.decodeObjectForKey("TempoPulseColor") as! NSData) as! UIColor! TimeBackGround = NSKeyedUnarchiver.unarchiveObjectWithData(aDecoder.decodeObjectForKey("TimeBackGround") as! NSData) as! UIColor! TimeStrokeColor = NSKeyedUnarchiver.unarchiveObjectWithData(aDecoder.decodeObjectForKey("TimeStrokeColor") as! NSData) as! UIColor! TextColor = NSKeyedUnarchiver.unarchiveObjectWithData(aDecoder.decodeObjectForKey("TextColor") as! NSData) as! UIColor! } func encodeWithCoder(aCoder: NSCoder!) { aCoder.encodeBool(lightEnabled, forKey: "lightEnabled") aCoder.encodeBool(soundEnabled, forKey: "soundEnabled") aCoder.encodeBool(vibrateEnabled, forKey: "vibrateEnabled") aCoder.encodeBool(pulseEnabled, forKey: "pulseEnabled") aCoder.encodeObject(songs, forKey: "songs") aCoder.encodeInteger(currentSong, forKey: "currentSong") aCoder.encodeBool(enableBackgroundSound, forKey: "enableBackgroundSound") aCoder.encodeBool(mixSound, forKey: "mixSound") aCoder.encodeBool(playSoundInBackground, forKey: "playSoundInBackground") aCoder.encodeBool(duckSounds, forKey: "duckSounds") aCoder.encodeObject(BPMBackground.archivedData(), forKey: "BPMBackground") aCoder.encodeObject(BPMPulseColor.archivedData(), forKey: "BPMPulseColor") aCoder.encodeObject(TempoBackGround.archivedData(), forKey: "TempoBackGround") aCoder.encodeObject(TempoPulseColor.archivedData(), forKey: "TempoPulseColor") aCoder.encodeObject(TimeBackGround.archivedData(), forKey: "TimeBackGround") aCoder.encodeObject(TimeStrokeColor.archivedData(), forKey: "TimeStrokeColor") aCoder.encodeObject(TextColor.archivedData(), forKey: "TextColor") } 

你应该创build一个结构或枚举组织你的密钥,因为你的方式只是容易打字。 只要把它放在你的class级之上

 enum Key: String { case allSettings case lightEnabled case soundEnabled } 

而不是像这样调用键

 ...forKey: Key.lightEnabled.rawValue) 

现在关于你的问题,我正面临同样的问题,我的游戏试图保存40个级别的属性(besttimes,级别解锁状态等)。 我最初做了你的尝试,这是纯粹的疯狂。

我结束了使用数组/字典,甚至是我的数据字典数组,减less了80%的代码。

还有什么好的呢,就是说你需要保存一些像LevelUnlock bools这样的东西,以后会让你的生活变得更加容易。 在我的情况下,我有一个UnlockAllLevelsbutton,现在我可以循环通过我的字典/数组,并更新/检查几行代码中的levelUnlock布尔。 比拥有大量if-else或switch语句来单独检查每个属性要好得多。

例如

  var settingsDict = [ Key.lightEnabled.rawValue: false, Key.soundEnabled.rawValue: false, ... ] 

比在解码器方法你说这个

注意:这种方式会考虑到您可能会将新值添加到SettingsDict中,而不是在下一个应用程序启动时,这些值将不会被删除,因为您不是用已保存的字典replace整个字典,而只是更新已经存在的值存在。

  // If no saved data found do nothing if var savedSettingsDict = decoder.decodeObjectForKey(Key.allSettings.rawValue) as? [String: Bool] { // Update the dictionary values with the previously saved values savedSettingsDict.forEach { // If the key does not exist anymore remove it from saved data. guard settingsDict.keys.contains($0) else { savedSettingsDict.removeValue(forKey: $0) return } settingsDict[$0] = $1 } } 

如果你使用多个词典比你的解码器方法将再次变得混乱,你也将重复许多代码。 为了避免这种情况,您可以使用generics创buildNSCoder的扩展。

  extension NSCoder { func decodeObject<T>(_ object: [String: T], forKey key: String) -> [String: T] { guard var savedData = decodeObject(forKey: key) as? [String: T] else { return object } var newData = object savedData.forEach { guard object.keys.contains($0) else { savedData[$0] = nil return } newData[$0] = $1 } return newData } } 

而且你可以在每个字典的解码器方法中写出这个。

 settingsDict = aDecoder.decodeObject(settingsDict, forKey: Key.allSettings.rawValue) 

你的编码器方法看起来像这样。

  encoder.encodeObject(settingsDict, forKey: Key.allSettings.rawValue) 

在你的游戏/应用程序中,你可以像这样使用它们

 settingsDict[Key.lightEnabled.rawValue] = true if settingsDict[Key.lightEnabled.rawValue] == true { /// light is turned on, do something } 

这种方式使得整合iCloud KeyValue存储(只是创build一个iCloud字典)也非常容易,主要是因为它很容易保存和比较很less的代码价值很多。

更新:

为了使这些调用更容易,我想在GameData类中创build一些便利的getter / setter。 这有一个好处,你可以更容易地在你的项目中调用这些属性(就像你以前的方式),但你的编码/解码方法仍然保持紧凑。 您还可以执行诸如循环比较值之类的操作。

  var isLightEnabled: Bool { get { return settingsDict[Key.lightEnabled.rawValue] ?? false } set { settingsDict[Key.lightEnabled.rawValue] = newValue } } var isSoundEnabled: Bool { get { return settingsDict[Key.soundEnabled.rawValue] ?? false } set { settingsDict[Key.soundEnabled.rawValue] = newValue } } 

比你可以称他们像正常的属性。

 isLightEnabled = true if isLightEnabled { /// light is turned on, do something }