是否有更好的方法将自定义类保存到NSUserDefaults而不是使用NSCoder编码和解码所有内容?

Arc*_*rch 1 archiving nsuserdefaults ios swift

我当前的类有大约50行只是编码和解码变量,以便我的类与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")
}
Run Code Online (Sandbox Code Playgroud)

cra*_*777 7

你应该创建一个struct或enum来组织你的密钥,因为你的方式很容易出现错别字.把它放在你的课堂上方

enum Key: String {
  case allSettings

  case lightEnabled
  case soundEnabled
}
Run Code Online (Sandbox Code Playgroud)

而不只是像这样调用键

...forKey: Key.lightEnabled.rawValue)
Run Code Online (Sandbox Code Playgroud)

现在关于你的问题,我的游戏尝试保存40个级别的属性(bestTimes,Level unlock status等)时遇到了同样的问题.我最初做了你尝试过的事情,这是纯粹的疯狂.

我最终使用数组/字典甚至字典数组来处理我的数据,这使我的代码减少了80%.

什么也很好,这就是说你需要保存像LevelUnlock bools这样的东西,它会让你以后的生活变得更轻松.在我的情况下,我有一个UnlockAllLevels按钮,现在我可以通过我的字典/数组循环,并在几行代码中更新/检查levelUnlock bools.比使用大量if-else或switch语句来单独检查每个属性要好得多.

例如

 var settingsDict = [
      Key.lightEnabled.rawValue: false, 
      Key.soundEnabled.rawValue: false, 
      ...
 ]
Run Code Online (Sandbox Code Playgroud)

比你在解码器方法中说的那样

注意:这种方式会考虑到您可能会向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
    }
} 
Run Code Online (Sandbox Code Playgroud)

如果您使用多个字典而不是解码器方法将再次变得混乱,您还将重复很多代码.为避免这种情况,您可以使用泛型创建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
     }
 }
Run Code Online (Sandbox Code Playgroud)

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

settingsDict = aDecoder.decodeObject(settingsDict, forKey: Key.allSettings.rawValue)
Run Code Online (Sandbox Code Playgroud)

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

 encoder.encodeObject(settingsDict, forKey: Key.allSettings.rawValue)
Run Code Online (Sandbox Code Playgroud)

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

settingsDict[Key.lightEnabled.rawValue] = true

if settingsDict[Key.lightEnabled.rawValue] == true {
  /// light is turned on, do something
}
Run Code Online (Sandbox Code Playgroud)

这种方式使得集成iCloud KeyValue存储也很容易(只需创建一个iCloud字典),这主要是因为它很容易保存并用很少的代码比较很多值.

更新:

为了使这些调用更容易,我想在GameData类中创建一些方便的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 }
}
Run Code Online (Sandbox Code Playgroud)

而且你可以像普通的属性一样称它们.

isLightEnabled = true

if isLightEnabled {
  /// light is turned on, do something
}
Run Code Online (Sandbox Code Playgroud)