首页 > 解决方案 > 为什么在我的代码中隐式解包 Optional 值时意外发现 nil?

问题描述

当我尝试使用另一个视图控制器将新任务保存在待办事项列表中时,我得到一个 Unexpectedly found nil ,同时在我的代码错误中隐式解包 Optional 值。当我点击一个按钮时,我打开了输入页面,然后有一个文本字段,我可以在其中输入文本以创建任务项。这是主视图控制器的代码:

  class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    
    @IBOutlet var tableView: UITableView!
    
    private var tasks = [TaskItem]()

    override func viewDidLoad() {
        super.viewDidLoad()
        getAllTasks()
        tableView.delegate = self
        tableView.dataSource = self
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tasks.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = tasks[indexPath.row].title
        return cell
    }
    
    @IBAction func didTapNewTask(){
        let viewContoller = storyboard?.instantiateViewController(identifier: "entry") as! EntryViewController
        viewContoller.title = "New Task"
        viewContoller.update = {
            DispatchQueue.main.async {
                self.getAllTasks()
            }
        }
        navigationController?.pushViewController(viewContoller, animated: true)
    }
    
    //Core Data Functions
    
    //Used to get all our tasks in our Core Data
    func getAllTasks() {
        do {
            tasks = try context.fetch(TaskItem.fetchRequest())
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }
        catch {
            print("error getting all tasks \(error)")
        }
    }
    
    //This is used to create a task, setting the properties to those in the parameters and then saving to our Core Data.
    func createTask(title: String, notes: String, difficulty: Int32) {
        let task = TaskItem(context: context)
        task.title = title
        task.notes = notes
        task.difficulty = difficulty
        task.dateCreated = Date()
        
        do {
            try context.save()
            getAllTasks()
        }
        catch {
            
        }
    }

这是入口视图控制器的代码:

class EntryViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var field: UITextField!
    
    var update: (() -> Void)?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        field.delegate = self
        // Do any additional setup after loading the view.
    }
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        saveTask()
        return true
    }
    
    @IBAction func saveTask(){
        let vc = storyboard?.instantiateViewController(identifier: "tasks") as! ViewController
        guard let text = field.text, !text.isEmpty else {
            let alert = UIAlertController(title: "Error", message: "Please input a title" , preferredStyle: UIAlertController.Style.alert)
            alert.addAction(UIAlertAction(title: "Confirm", style: UIAlertAction.Style.default, handler: nil))
            self.present(alert,animated: true,completion: nil)
            return
        }
        vc.createTask(title: text, notes: "Hello", difficulty: 10)
        
        update?()
        
        navigationController?.popViewController(animated: true)
    
    }

一旦我点击保存新任务,应用程序就会崩溃,但是一旦我重新加载应用程序,我刚刚创建的任务就在那里。

标签: iosswift

解决方案


@IBAction func saveTask(){
        let vc = storyboard?.instantiateViewController(identifier: "tasks") as! ViewController
        guard let text = field.text, !text.isEmpty else {
            let alert = UIAlertController(title: "Error", message: "Please input a title" , preferredStyle: UIAlertController.Style.alert)
            alert.addAction(UIAlertAction(title: "Confirm", style: UIAlertAction.Style.default, handler: nil))
            self.present(alert,animated: true,completion: nil)
            return
        }
        vc.createTask(title: text, notes: "Hello", difficulty: 10)
        
        update?()
        
        navigationController?.popViewController(animated: true)
    
    }

此方法的第一行是您的问题的根源。您在这里所做的是创建原始视图控制器的实例,而不是您最初来自的实例。

这种工作暂时有效,因为您随后调用createTask该视图控制器来执行您的新任务。这很好,但是该方法然后调用getAllTasks,然后调度到主队列,然后调用重新加载表上的数据。

但是您的表不存在,因为这是视图控制器的一个实例,它从未加载过它的视图。表格视图是一个隐式展开的可选项,但是当你在这里点击它时它是 nil。

您最好的解决方案是传入一个块(就像您使用更新一样)来创建一个新任务,并在该块中调用原始视图控制器上的方法。


推荐阅读