在 UserDefaults 中设置对象数组

Posted

技术标签:

【中文标题】在 UserDefaults 中设置对象数组【英文标题】:Set array of objects in UserDefaults 【发布时间】:2019-02-18 11:27:43 【问题描述】:

如何在UserDefaults中设置和获取json对象数组?

当我尝试如下设置时,我的应用程序崩溃了:

import SwiftyJSON
var finalArray = [JSON]()

UserDefaults.standard.set(finalArray, forKey: "attemptedArray")

我的数据如下:

[
  "marked" : 3,
  "attempted" : true,
  "correct" : 3,
  "subject" : 1,
  "status" : true,
  "question" : 219,
  "answer" : 32931,
  "time" : 15,
  "score" : 5,
  "chapter" : 26
, 
  "marked" : 4,
  "attempted" : true,
  "correct" : 4,
  "subject" : 1,
  "status" : true,
  "question" : 550,
  "answer" : 34256,
  "time" : 23,
  "score" : 10,
  "chapter" : 26
, 
  "marked" : 1,
  "attempted" : true,
  "correct" : 1,
  "subject" : 1,
  "status" : true,
  "question" : 566,
  "answer" : 34317,
  "time" : 33,
  "score" : 14,
  "chapter" : 26
]

【问题讨论】:

"我的应用程序崩溃" 错误信息是什么? JSON 似乎不符合 NSCoding 标准,您无法将任何您想要的内容保存到 UserDefaults 中(您需要将其转换为允许的类型)。检查它的文档。 它无法保存用户创建的类的对象...您正在保存“JSON”的对象,将其转换为字典,然后您可以保存 您有名为finalArr 的变量,但您正在设置finalArray 。请使用正确的变量。 你可以转换成[[String : Any]]? 这是在滥用用户默认设置。请不要滥用用户默认设置。 【参考方案1】:

UserDefaults 无法保存 SwiftyJSON 的 JSON 类型。你必须保存一些他们支持的类型,在这种情况下,你正在寻找Data

无论如何,将Data 保存到UserDefaults 并不是最好的,您应该将Data 保存到其他地方。为此,您可以使用FileManager


因此,为您的数据创建自定义模型,而不是使用 JSON 并将 Codable 应用于您的自定义模型

struct Model: Codable 
    var marked, correct, subject, question, answer, time, score, chapter: Int
    var attempted, status: Bool

那么你应该使用 Model 作为数组中的元素类型(请注意,你需要使用 JSONDecoder 解码来自 Data 的响应(见下文))

var finalArray = [Model]()

然后您可以使用JSONEncoder 将您的模型数组编码为Data,您可以将其写入某个文件

do 
    let encoded = try JSONEncoder().encode(finalArray)
    let preferencesDirectoryURL = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first!.appendingPathComponent("Preferences", isDirectory: true)
    let fileURL = preferencesDirectoryURL.appendingPathComponent("fileName.json")
    try encoded.write(to: fileURL)
 catch  print(error) 

JSONDecoder 用于从保存的文件中解码Data

do 
    let preferencesDirectoryURL = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first!.appendingPathComponent("Preferences", isDirectory: true)
    let fileURL = preferencesDirectoryURL.appendingPathComponent("fileName.json")

    if let data = try? Data(contentsOf: fileURL) 
        let decoded = try JSONDecoder().decode([Model].self, from: data)
    
 catch  print(error) 

【讨论】:

Codable 实现很好。它不是将数据保存到 UserDefaults。意思是只存储App的界面状态。它不适用于存储一般应用程序数据。您可以将数据写入磁盘到单独的文件.json(这只是一个正确格式化的简单文本文件)到 UserDefaults 用来存储其首选项文件Library/Preferences***.com/a/34701970/2303865的同一目录中。 相关developer.apple.com/library/archive/documentation/…【参考方案2】:
do
            try let data = NSKeyedArchiver.archivedData(withRootObject: finalArray, requiringSecureCoding: false)
    UserDefaults.standard.set(data, forKey: "attemptedArray")
        catch
            print("Print exception here")
        

阅读:-Property List Programming Guide

【讨论】:

以 NSException 类型的未捕获异常终止 @DeepakVerma,请参阅更新后的答案。您需要使用符合 ios 12 的最新语法【参考方案3】:

由于某些原因,不建议在 NSuser 默认值中保存大量数据:

    如果您要删除阵列的某些单元,您需要从 nsuser 默认值中删除它们。只有将它们保存为 数组而不是个体。

    如果您选择在每次删除或添加一项时将它们保存为数组,则需要将它们全部保存。如果数组很大,可能需要一些时间

    任何人都可以访问 NSuser 默认值。所以不安全

无论如何,我真的建议使用核心数据。它为您省去了我提到的所有麻烦。

【讨论】:

以上是关于在 UserDefaults 中设置对象数组的主要内容,如果未能解决你的问题,请参考以下文章

如何在反应中的对象数组中设置对象数组的状态

SwiftUI 在 UserDefaults 中设置字符串值,但作为`EnvironmentObject`

如何在数组中设置特定对象的状态

React:在 setState 中设置数组对象

在 Swift 3 中设置 UserDefaults 的问题

在 Ionic 5 中设置数组对象的存储