可选返回nil,即使它包含非nil值

时间:2019-09-20 03:30:22

标签: ios swift enums protocols

我有一个customStringConvertible枚举,我曾经为将用于collectionViewCells的数据建模。打算在cellForItemAt检索数据。未检索数据。

制定了将可获取的var附加到枚举的协议。

初始化var并且值即使在打印语句显示预期值时也返回nil。

更新*** 我将数据重组为一个类,并将其添加到数组中。然后,我通过向数据库检索功能中添加如下代码行来解决了该问题:

DispatchQueue.main.async {
   //collectionView.reloadData()
}

我在if let语句完成后立即输入了代码。我按原样保留了下面的代码。将数据库代码放入其自己的单独函数中,嵌入相应控制器的viewDidLoad函数中。

var name: String {
        var name:String?
        let uid = Auth.auth().currentUser?.uid
        Database.database().reference().child("users").child(uid!).observeSingleEvent(of: .value, with: { (snapshot) in
            if let dictionary = snapshot.value as? [String:AnyObject] {
                name = dictionary["firstName"] as? String
                print(String(describing: name))
            }
        }, withCancel: nil)
        return name ?? "Does not Work"
    }

当为可重用单元格的属性而不是var调用"Does not Work"名称时,预期结果将显示快照值

2 个答案:

答案 0 :(得分:0)

我认为您应该将异步块移到另一个函数,然后在获取其值之后更新名称,因为@rmaddy指出return name ?? "Does not Work"在异步块完成之前已被调用。

    func updateName() {
        let uid = Auth.auth().currentUser?.uid
Database.database().reference().child("users").child(uid!).observeSingleEvent(of: .value, with: { (snapshot) in
                if let dictionary = snapshot.value as? [String:AnyObject] {
                    name = dictionary["firstName"] as? String
                    // update the new name to your model and update your label with the new name
                }
            }, withCancel: nil)
    }

获取名称后,您还可以使用回调来更新UI:

    func updateName(callback: (String) -> ()) {
        let uid = Auth.auth().currentUser?.uid
Database.database().reference().child("users").child(uid!).observeSingleEvent(of: .value, with: { (snapshot) in
                if let dictionary = snapshot.value as? [String:AnyObject] {
                    name = dictionary["firstName"] as? String
                    // update the new name to your model and update your label with the new name
                    // call the callback function
                    callback(name)
                }
            }, withCancel: nil)
    }

从外部调用updateName函数:

updateName() { (name) in
    label.text = name
}

答案 1 :(得分:0)

您的问题是,您正在尝试访问一个变量,该变量的值实际上取决于其中的完成处理程序。

因此,当完成处理程序未触发时,应用程序有时会给您有时,而在调试时,您会看到一个值,因为该值现在存在,但问题是编译器现在可以读取其他代码行,并且不知道该值在这里。

换句话说:您正在尝试访问从某个地方获取的某些值,并且在等待该值返回并正确使用它的同时,编译器现在正在另一个线程上读取其他内容。

您缺少的是一个完成处理程序,该处理程序实际上在该变量的取值源于其获取位置时会触发该变量的值,因此迫使编译器在触发时返回并读取它。

  

注意::我建议阅读this文章,内容涉及完成处理程序以及它们实际如何快速运行。

如何解决您的问题:

由于您使用的是enum,并且由于某些原因您希望获取函数本身在变量中,因此只能通过使用DispatchSemaphores创建一个临界区来解决此问题。

我为您创建了一个小型演示,以了解其工作原理,同时我还推荐this文章。

演示:

 var name: String {
    let semaphore = DispatchSemaphore(value: 0)
    var name = "no value"
        DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) {
            name = "new Value"
            semaphore.signal()
    }
    semaphore.wait()
    return name
}

您的代码应该是这样的。

  var name: String {
        let semaphore = DispatchSemaphore(value: 0)
        var name:String?
        let uid = Auth.auth().currentUser?.uid
        Database.database().reference().child("users").child(uid!).observeSingleEvent(of: .value, with: { (snapshot) in
            if let dictionary = snapshot.value as? [String:AnyObject] {
                name = dictionary["firstName"] as? String
                print(String(describing: name))
                semaphore.signal() // signal when i get the new value
            }
        }, withCancel: nil)
        semaphore.wait() // wait till signal
        return name ?? "Does not Work"
    }
  

请注意,这是处理您案件的唯一方法,不建议这样做,我愿意   建议更改您的结构,因为您将冻结   应用程序,直到您获得数据,而且数据很糟糕。