ios - 线程 1:致命错误:当我尝试使用 NSuserDefaults 保存对象时,在展开可选值时意外发现 nil
问题描述
我正在尝试在我的 userdefaults 中保存一个类的实例,我使用以下方法来执行此操作,并且在我的类中添加 isImportant 属性后它似乎不起作用。这是我的代码
class Task: NSObject , NSCoding {
var taskDate:String
var task:String
var isImportant:Bool
init(task: String , taskDate: String , isImportant: Bool){
self.task = task
self.taskDate = taskDate
self.isImportant = isImportant
}
required convenience init?(coder aDecoder: NSCoder) {
guard let taskDate = aDecoder.decodeObject(forKey: "taskDate") as? String ,
let task = aDecoder.decodeObject(forKey: "task") as? String ,
let importantdecode = aDecoder.decodeObject(forKey: "importantBool") as? Bool else {
return nil
}
self.init(task: task, taskDate: taskDate , isImportant:importantdecode)
}
func encode(with aCoder: NSCoder) {
aCoder.encode(taskDate, forKey: "taskDate")
aCoder.encode(task, forKey: "task")
aCoder.encode(isImportant, forKey: "importantBool")
}
}
上面的代码是我的课,我添加了一些协议,老实说我不知道它们是如何正常工作的,感谢 ofc 的澄清。
现在这是我面临的问题。
var tasks = [Task]()
let defaults = UserDefaults.standard
let defaultskey:String = "save"
et task:Task = Task(task: taskTextField.text!, taskDate: now ,isImportant: false )
tasks.append(task)
// Calling the saveTask Function
saveTasks()
// Calling the shake function in the custombutton class since we declared the sender here to be CustomButtom
sender.shake()
}
override func viewDidLoad() {
super.viewDidLoad()
// Getting the permanent Storage
loadTasks()
// Saving array of objects with userDefaults
func saveTasks() {
do {
let taskDataWorking = try NSKeyedArchiver.archivedData(withRootObject: tasks, requiringSecureCoding: false) // archiving data for object to be able to store it.
defaults.set(taskDataWorking, forKey: defaultskey)
//throw MyError.FoundNil("xmlDict")
} catch {
print(error)
}
}
// Loading the data
func loadTasks(){
let taskData = defaults.object(forKey: defaultskey) as? NSData
if let taskData = taskData {
do {
let taskArray = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(taskData as Data) as? [Task] // unarchiving data as and loading it
** The error occurs here ** tasks = taskArray! // assigning the tasks to the archived value when saved so when it launched we will be back at the last saved tasks
tasksAccomplished = taskArray!.count
} catch {
print("error while loading")
}
}
}
解决方案
崩溃的表面原因在这里:
let taskArray = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(taskData as Data) as? [Task]
tasks = taskArray!
您正在强制解开可选的taskArray
. 如果taskArray
是,nil
那么您的程序会崩溃。
使用条件展开更安全,或者您可以使用 nil 合并运算符来提供默认值。
if let taskArray = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(taskData as Data) as? [Task] {
tasks = taskArray
} else {
// Unable to load tasks - do something
}
或者
let taskArray = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(taskData as Data) as? [Task]
tasks = taskArray ?? []
真正的问题是,为什么初始化程序失败了?为什么是taskArray
零?
答案在于这一行:
let importantdecode = aDecoder.decodeObject(forKey: "importantBool") as? Bool else {
return nil
}
密钥importantBool
用于编码 Bool,而不是对象。
As isImportant
is a Bool
, aCoder.encode(isImportant, forKey: "importantBool")
is call encode(_ value: Bool, forKey: String)
。
这可能会令人困惑。由于 Swift 知道您正在编码的值的类型,它可以调用正确的encode
. 当您解码时,它不知道与密钥关联的类型。您负责调用正确的decode...
函数。
你要
guard let taskDate = aDecoder.decodeObject(forKey: "taskDate") as? String ,
let task = aDecoder.decodeObject(forKey: "task") as? String else {
return nil
}
let importantDecode = aDecoder.decodeBool(forKey: "importantBool")
self.init(task: task, taskDate: taskDate , isImportant:importantdecode)
请注意,不像decodeObject
,decodeBool
不返回可选项,因此它不能成为guard
语句的一部分。
您仍然应该进行防御性编码,而不是强制 unwrap taskArray
。
推荐阅读
- pyinstaller - conda pyinstaller 构建的 exe 在 windows server 2016 中不起作用,从 mkl_intel_threding.dll 中找不到 kernalbase.dll 异常模块
- jquery - 如何在jquery中动态添加onclick事件
- python - 如何以键值对格式将以下字符串添加到字典中
- javascript - 目标元素未在选项卡窗格中显示活动类
- javascript - 从 Java 应用程序在 Safari 中打开的 Html 报告显示为好像未启用 Javascript
- javascript - 无法在Javascript中映射分组数组
- python - 获取熊猫中每个不同组的事件总和
- c# - C#/C++ 传递结构得到访问冲突异常
- html - 锚标记打开一个新选项卡,而不是在 Blazor 服务器应用程序中下载文件
- unity3d - 已上传 AAB。没有权限但是,尝试更改目标错误:您必须删除位置权限请求