带有可选选项的致命错误没有意义

Posted

技术标签:

【中文标题】带有可选选项的致命错误没有意义【英文标题】:fatal errors with optionals not making sense 【发布时间】:2021-02-15 14:52:54 【问题描述】:

我不断收到一个致命错误,说明一个值是如何解包的,它是 nil,我不明白如何解包。当我用特定变量实例化视图控制器时,它们都会显示出来,但是当我对确切的 VC 执行 segue 时,值不会显示出来。

以这些函数为例...

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
    tableView.deselectRow(at: indexPath, animated: true)
    
  
    
    if let displayVC = storyboard?.instantiateViewController(withIdentifier: Constants.Storyboards.TeachStoryboardID) as? SchoolEventDetailsViewController 
        
        displayVC.selectedEventName = events[indexPath.row].eventName
        displayVC.selectedEventDate = documentsDate[indexPath.row].eventDate
        displayVC.selectedEventCost = documentsCost[indexPath.row].eventCost
        displayVC.selectedEventGrade = documentsGrade[indexPath.row].eventGrade
        displayVC.selectedEventDocID = documentsID[indexPath.row]?.docID
        
        navigationController?.pushViewController(displayVC, animated: true)
    

结合这个功能:

func verifyInstantiation() 
    if let dateToLoad = selectedEventDate 
        dateEditableTextF.text = dateToLoad
    
    
    if let costToLoad = selectedEventCost 
        costEditableTextF.text = costToLoad
    
    
    if let gradesToLoad = selectedEventGrade 
        gradesEditableTextF.text = gradesToLoad
    
    
    if let docIDtoLoad = selectedEventDocID 
        docIDUneditableTextF.text = docIDtoLoad
    
    
    if let eventNameToLoad = selectedEventName 
        eventNameEditableTextF.text = eventNameToLoad
    

帮助完美加载数据,但是当我尝试从搜索控制器执行 segue 时,数据不存在。

override func viewDidLoad() 
    super.viewDidLoad()
    
    navigationItem.title = selectedEventName

我将 vc 的标题设置为事件名称,并且我最近还添加了一个文本字段来存储它以用于实验目的(这个问题)。

现在的问题是我想从 Algolia 搜索控制器向该 VC 进行数据传输,我得到了所有其他字段来显示,除了一个是文档 ID。所以我创建了一个完成处理函数,将文档 ID 作为字符串获取,并在执行 segue 时将其插入到 vc 中,就像实例化 vc 时一样。

这里是函数:

func getTheEventDocID(completion: @escaping ((String?) -> ())) 
    documentListener = db.collection(Constants.Firebase.schoolCollectionName).whereField("event_name", isEqualTo: selectedEventName ?? navigationItem.title).addSnapshotListener(includeMetadataChanges: true)  (querySnapshot, error) in
        if let error = error 
            print("There was an error fetching the documents: \(error)")
         else 
            self.documentsID = querySnapshot!.documents.map  document in
                return EventDocID(docID: (document.documentID) as! String)
            
            let fixedID = "\(self.documentsID)"
            let substrings = fixedID.dropFirst(22).dropLast(3)
            let realString = String(substrings)
            completion(realString)
        
    

我认为selectedEventNamenavigationItem.title 将完成工作并在我现在将展示的数据传输函数中使用该函数时提供值:

 //MARK: - Data Transfer From Algolia Search to School Event Details
override func prepare(for segue: UIStoryboardSegue, sender: Any?) 

    
    otherVC.getTheEventDocID  (eventdocid) in
        if let id = eventdocid 
            if segue.identifier == Constants.Segues.fromSearchToSchoolEventDetails 
                let vc = segue.destination as! SchoolEventDetailsViewController
                vc.selectedEventName = self.nameTheEvent
                vc.selectedEventDate = self.dateTheEvent
                vc.selectedEventCost = self.costTheEvent
                vc.selectedEventGrade = self.gradeTheEvent
                vc.selectedEventDocID = id
            
        
    
 
    

但是当点击搜索结果时它最终什么也没有显示,这非常令人沮丧,我不明白为什么当我在SchoolEventDetailsVC 中声明它们时它们都是空值。我试图强制打开 selectedEventName 并崩溃说有一个 nil 值,我不知道为什么。这个问题实际上还有很多,但我只是尽量保持简短,以便人们真正尝试阅读它并提供帮助,因为没有人阅读我发布的问题,所以是的,提前谢谢。

【问题讨论】:

getTheEventDocID 是做什么的?如果它是异步工​​作的,可能是代码执行得太晚了。 getTheEventDocID 获取 Firebase 生成的事件文档 ID 作为字符串。它确实是异步工作的,有没有办法防止代码执行得太晚? @AndreasOetjen 【参考方案1】:

我有点困惑otherVC 是什么,它在getTheEventDocID 中设置自己的属性,而在闭包中设置self 的属性,这是一个不同的控制器。不过没关系,我希望你知道自己在做什么。

由于getTheEventDocID 异步运行,视图将在数据可用之前加载和显示。因此,viewDidLoad 看不到实际数据,而是很快就会过时的数据。

因此,您需要通知细节视图控制器新数据可用,并刷新其用户界面。类似的东西

override func prepare(for segue: UIStoryboardSegue, sender: Any?) 
    otherVC.getTheEventDocID  (eventdocid) in
        if let id = eventdocid 
            if segue.identifier == Constants.Segues.fromSearchToSchoolEventDetails 
                let vc = segue.destination as! SchoolEventDetailsViewController
                vc.selectedEventName = self.nameTheEvent
                vc.selectedEventDate = self.dateTheEvent
                vc.selectedEventCost = self.costTheEvent
                vc.selectedEventGrade = self.gradeTheEvent
                vc.selectedEventDocID = id
                vc.updateUI()
            
        
    

在目标视图控制器中:

class SchoolEventDetailsViewController ... 
    override func viewDidLoad() 
        super.viewDidLoad()
        updateUI()
    

    func updateUI () 
        navigationItem.title = selectedEventName
        // and so on
    

【讨论】:

otherVC 是在实例化时获取事件详细信息的 vc,我无法在当前 tableVC 中调用getTheEventDocID,因为该 vc 没有存储信息,但我会试一试。 @Andreas Oetjen 是的,它仍然会因 nil 错误而崩溃。 @Andreas Oetjen 如果我把它放在 viewWillAppear 中会怎样?会有什么不同吗? @Andreas Oetjen 但是它到底在哪里崩溃了?在 updateUI() 中还是之前?调用堆栈是什么?【参考方案2】:

好的,所以我决定尝试一种解决方法并完全放弃 getTheEventDocID() 方法,因为它只会给我带来压力。所以我决定放弃 Firebase 生成的文档 IDS,只使用我制作的函数中生成的 10 位数字的 ID。我还想出了如何在 Algolia 记录中添加完全相同的 10 位 id,只需将随机的 10 位 id 存储在一个变量中并在两个地方都使用它。所以现在我没有使用查询调用来获取 Firebase 生成的文档 ID 并在每次单击搜索结果时让我的应用程序崩溃,我基本上编辑了 Algolia 记录的 Struct 并添加了一个可以使用的 eventDocID 属性hits.hitSource(at: indexPath.row).eventDocID

现在就像我通过 segue 数据传输将其他字段添加到 vc 一样,我现在可以对我的文档 ID 做同样的事情,因为一切都匹配:)。

【讨论】:

以上是关于带有可选选项的致命错误没有意义的主要内容,如果未能解决你的问题,请参考以下文章

致命错误:将可选值 TableViewCell 展开到 UIViewController 时发现 Nil

如何在展开可选值时调试“nil 的致命错误”?

致命错误:在展开可选值 (lldb) 时意外发现 nil

Swift 中的非英文字符给出:致命错误:在展开可选值时意外发现 nil

致命错误:在展开可选值错误 Swift 时意外发现 nil

致命错误:在过滤时展开可选值时意外发现 nil