Необязательный возврат nil, даже если он содержит ненулевое значение - PullRequest
0 голосов
/ 20 сентября 2019

У меня есть customStringConvertible перечисление, которое я использовал для моделирования данных, которые будут использоваться для collectionViewCells.Данные предназначены для извлечения в cellForItemAt.Данные не извлекаются.

Составлен протокол для добавления переменных entable в перечисление.

Инициализированный var и value возвращают ноль, даже если оператор print показывает предполагаемые значения.

Обновление *** Я реструктурировал свои данные в класс и добавил их в массив.И я решил эту проблему, добавив строку кода в функцию поиска в базе данных следующим образом:

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 голосов
/ 20 сентября 2019

Ваша проблема в том, что вы пытаетесь получить доступ к переменной, значение которой на самом деле зависит от обработчика завершения внутри нее.

Поэтому приложение дает вам nil иногда , когда обработчик завершения не сработал, с другой стороны, при отладке вы можете увидеть значение, потому что значение теперь существует, но проблемаЭто компилятор теперь может читать другие строки кода, и он не знает, что значение теперь здесь.

Другими словами: вы пытаетесь получить доступ к некоторому значению, которое вы извлекаете откуда-то, и пока вы ждете, чтобы это значение вернулось и правильно его использовали, компилятор теперь читает что-то другое в другом потоке.,

Что вам не хватает, так это обработчик завершения, который фактически запускает значение этой переменной, когда оно исходит из того места, где вы ее выбираете, поэтому вынуждает компилятор возвращаться и читать его, когда он срабатывает.

Примечание: Я рекомендую прочитать эту статью о обработчиках завершения и о том, как они на самом деле функционируют в swift.

Как исправить ваш случай:

Поскольку вы используете enum, и вам нужна функция извлечения внутри самой переменной, по какой-то причине вы можете исправить это, только создав критическую секцию.с DispatchSemaphores.

Я создал небольшую демонстрацию для вас, чтобы понять, как она работает, и я также рекомендую эту статью.

Демо:

 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"
    }

Остерегайтесь, это единственный способ для вашего случая, и он не рекомендуется, я рекомендую изменить вашу структуру, потому что вы будете замораживать приложение, пока не получите данные, и этоэто плохо.

0 голосов
/ 20 сентября 2019

Я думаю, вам следует переместить асинхронный блок в другую функцию, а затем обновить имя после получения его значения, поскольку @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)
    }

Вы также можете использовать обратный вызов для обновления пользовательского интерфейса после получения имени:

    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
}
...