2016-03-06 71 views
1

我现在的类有大约50行只是编码和解码变量,以便我的类与NSUserDefaults兼容。有没有更好的方法来处理这个问题?有没有比NSCoder编码和解码所有东西更好的方式来保存自定义类到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") 
} 
+0

你见过尼克洛克伍德的https://github.com/nicklockwood/FastCoding吗? – Shripada

+0

如果你很好奇,我会用最近刚开始做的一件小事来稍微更新我的答案。在我的回答结束时检查更新的位。我相信它会改善你的代码甚至模式。 – crashoverride777

+0

嘿,请再次阅读我的答案,主要是有关解码器方法的部分。我注意到那里有一个错误。我没有考虑到您可能会向SettingsDict添加新值,而不是在下次应用启动时将这些值删除,因为整个字典会被保存的字典所取代。我现在只用保存的数据更新字典。这应该会使它在未来更加灵活。 – crashoverride777

回答

6

你应该创建一个结构或枚举来组织你的钥匙,因为你的办法就是容易拼写错误。只要把它的权利类以上

enum Key: String { 
    case allSettings 

    case lightEnabled 
    case soundEnabled 
} 

,并不仅仅是调用键,像这样

...forKey: Key.lightEnabled.rawValue) 

现在关于你的问题,我面临着同样的问题,我的游戏试图挽救房产40级(besttimes,级别解锁状态等)。我最初做了你的尝试,这纯粹是疯狂。

我最终使用了数组/字典,甚至是我的数据的字典数组,我的数据减少了80%。

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

例如

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

比解码方法,你说这

注意:此方法会考虑到你可能会添加新值SettingsDict,比对下一个应用程序启动相应的帐户值不会被删除,因为您没有用保存的字典替换整个字典,只更新已存在的值。

// 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 
    } 
} 

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

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键值存储(刚创建的iCloud字典),又主要是因为其所以很容易保存并用很少的代码比较很多值。

UPDATE:

要调用这些更容易一点我喜欢创造一些便利的getter /在GAMEDATA类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 
} 
+0

谢谢!这个解释很好,完美地回答了我的问题。 – Arch

+0

不客气。快乐的编码 – crashoverride777

相关问题