2016-03-06 68 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

相關問題