有没有比使用 NSCoder 编码和解码所有内容更好的方法将自定义类保存到 NSUserDefaults 中?

2023-12-05

我当前的类大约有 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")
}

您应该创建一个结构体或枚举来组织您的键,因为您的方式很容易出现拼写错误。只需将其放在班级上方即可

enum Key: String {
  case allSettings

  case lightEnabled
  case soundEnabled
}

而不是像这样调用按键

...forKey: Key.lightEnabled.rawValue)

现在关于你的问题,我在尝试保存 40 个级别的属性(最佳时间、级别解锁状态等)时遇到了同样的问题。我最初做了你尝试过的事情,这纯粹是疯狂的。

我最终使用数组/字典甚至字典数组来存储数据,这将我的代码减少了 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 KeyValue 存储变得非常容易(只需创建一个 iCloud 字典),这主要是因为它可以轻松地用很少的代码保存和比较大量值。

UPDATE:

为了使调用这些更容易一些,我喜欢在 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 }
}

你可以像普通属性一样称呼它们。

isLightEnabled = true

if isLightEnabled {
  /// light is turned on, do something
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

有没有比使用 NSCoder 编码和解码所有内容更好的方法将自定义类保存到 NSUserDefaults 中? 的相关文章

  • 如何观察UserDefaults的变化?

    我有一个 ObservedObject在我看来 struct HomeView View ObservedObject var station Station var body some View Text self station sta
  • ReactNative - 未处理的 JS 异常:SyntaxError

    当我尝试在 iOS 8 上启动 RUN 应用程序时 出现这个奇怪的错误 Unhandled JS Exception SyntaxError仅此而已 不再有更多信息 有any1偶然发现这个问题吗 在 iOs 9 上应用程序运行正常 x代码版
  • 如何检测 swiftui 中是否存在键盘

    我想知道按下按钮时键盘是否存在 我该怎么做 我已经尝试过 但我没有任何运气 谢谢 使用该协议 KeyboardReadable 你可以符合任何View并从中获取键盘更新 KeyboardReadable协议 import Combine i
  • 如何在 swiftUI (macOS) 中检测按键按下和释放

    除了标题之外没什么可说的 我希望能够在按下按键和释放按键时 在 macOS 上 在 swiftUI 视图中执行操作 在 swiftUI 中是否有任何好的方法可以做到这一点 如果没有 有什么解决方法吗 不幸的是 键盘事件处理是其中一个令人痛苦
  • 为什么在迭代字典时会出现“类型 [object] 的值没有成员 'lowercaseString'” 错误? [关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 我有几个对象 Struct object var title String var one object green v
  • 对于使用 CCCrypt() 的 AES128,密钥可以长于 128 位吗?

    我正在使用CCCrypt https developer apple com library archive documentation System Conceptual ManPages iPhoneOS man3 CCCrypt 3c
  • Xcode 错误 - 架构 x86_64 的未定义符号?

    我正在运行 Swift 4 和 Xcode 9 beta 我收到此错误 但我不知道如何解决它 我什至不知道这是什么意思 Undefined symbols for architecture x86 64 T0So22AVCapturePho
  • 删除派生数据文件夹后,Xcode 不断重新创建派生数据文件夹

    自动完成功能在 Xcode 6 中不再起作用 我四处搜索 发现删除派生数据文件夹可以解决此问题 每次我删除它时 它都会回来 然后就不会再自动完成了 有什么建议么 Thanks 没关系 我解决了这个问题 我没有声明需要在类内的方法中使用的变量
  • HTML 分页

    有没有html分页的开源项目 我正在为 iPhone 开发一个应用程序 我想在 UIWebView 上显示 HTML 文件 并且不希望用户向下滚动以查看屏幕上未显示的剩余内容 我想在第二个 UIWebView 上显示剩余的内容 我怎样才能做
  • 我们能否检测用户是否通过主页按钮或锁定按钮离开而没有监听 darwin 通知?

    我最近向应用程序商店提交了一个新的二进制文件并将其发送以供审核 但它立即被拒绝并显示以下消息 不支持的操作 不允许应用程序监听设备锁定通知 经过一番挖掘后 我发现我们无法使用 com apple springboard lockstate
  • 为什么 UITableViewCell 不可访问(对于 VoiceOver)

    我并不是想解决任何问题 当然你可以设置isAccessibilityEnabled true它有效 我的问题是 为什么它默认关闭并且界面生成器中没有适当的部分 在我看来 不建议使 UITableViewCell 子类可访问 有没有更好的方法
  • 为 Swift 对象/属性设置观察者

    我一直在寻找一种在连接到 Mac 的显示器数量发生变化时触发方法的方法 我知道我可以获得 NSScreen screens count 的值 但我需要找到一种方法来在该值发生变化时创建通知或其他内容 或者指示所连接的显示器数量发生变化的其他
  • iOS:滚动视图仅在键盘出现后才起作用

    我制作了滚动视图 其中有很多文本字段 我添加了更新的 TPKeyBoardAvoidingScrollView 并将其添加到滚动视图的文件所有者中 我在 h 文件中添加了插座 在 m 文件中综合并添加了行 self view addSubv
  • 如何右对齐 UILabel?

    Remark 实施 myLabel textAlignment right does not解决了我的问题 这不是我所要求的 我想要实现的是让标签对齐右对齐 为了更清楚地说明 这就是如何left对齐外观 就是这样justify对齐外观 if
  • 对成员“buildBlock()”的引用不明确

    我一直在尝试使用 Swift UI 为 iOS 13 制作一个应用程序 但我不断收到这个奇怪的错误 对成员 buildBlock 的引用不明确 无论我做什么 错误都不会消失 我尝试一次对代码段进行注释 以查看哪一部分可能导致了问题 但唯一有
  • 从 iOS 13 开始安排 iOS 后台任务

    我正在实现用于更新数据的BackgroundTasks 框架 但我遇到了以下问题 无法计划刷新App 错误域 BGTaskSchedulerErrorDomain代码 1 空 无法安排数据提取 Error Domain BGTaskSche
  • 以弯曲格式显示文本

    我正在寻找以曲线格式绘制一些文本 我使用哪个控件并不重要 UITextField UILabel or UITextView 我只想显示如图所示的文本 仍在寻找解决方案 请帮忙 查看此链接 https nodeload github com
  • NSTimer 不触发选择器

    在带有 ARC 的 ios5 0 中 在我的 rootviewcontroller 中 我调用由应用程序委托持有的安全管理器对象中的方法 在该方法中 我设置计时器如下 NSTimer timer NSTimer scheduledTimer
  • UITableView 滑动删除 iOS 上的手势冲突

    我的手势识别器有问题 我的目标是在表视图中实现使用滑动删除 但我认为其他手势是相互冲突的 我正在使用这个库romonthego REFrostedViewController https github com romaonthego REF
  • 设置/覆盖 UICollectionView 中单元格之间的填充

    我有一个 UICollectionView 但在获取单元格之间的填充时遇到了问题 理论上 我应该能够将屏幕除以 4 并且我可以获得包含 4 个图像的单元格大小 完美地占据屏幕宽度 但是 它选择不这样做 相反 它会创建 3 个具有巨大填充的图

随机推荐