首页 > 解决方案 > 线程 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")
            }

        }
    }

标签: iosswift

解决方案


崩溃的表面原因在这里:

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 isImportantis 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


推荐阅读