立即从Firebase块访问值/避免竞争条件

时间:2017-05-01 02:01:06

标签: swift firebase firebase-realtime-database boolean

我有一个bool" hasHaveProfile"我在while循环中更改为TRUE然后我退出循环...但是布尔值仍然没有变化。有什么想法吗?

if AccessToken.current != nil {
        fetchUserData()
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3.5) {
            let dref = FIRDatabase.database().reference().child("CheckUsers")
            dref.observeSingleEvent(of: .value, with: { (snapshot: FIRDataSnapshot) in
                var alreadyHaveProfile = false
                let enumerator = snapshot.children
                loop: while let user = enumerator.nextObject() as? FIRDataSnapshot {
                    if user.key == userID {
                        alreadyHaveProfile = true
                        break loop
                    }
                }
            }, withCancel: { (error: Error) in
                print(error.localizedDescription)
            })
            print(alreadyHaveProfile)

**更新的答案:

if AccessToken.current != nil {
        fetchUserData()
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3.5) {
            let dref = FIRDatabase.database().reference().child("CheckUsers")
            dref.observeSingleEvent(of: .value, with: { (snapshot: FIRDataSnapshot) in
                var alreadyHaveProfile = false
                let enumerator = snapshot.children
                loop: while let user = enumerator.nextObject() as? FIRDataSnapshot {
                    if user.key == userID {
                        alreadyHaveProfile = true
                        self.finishSetUp()
                        break loop
                    }
                }
            }, withCancel: { (error: Error) in
                print(error.localizedDescription)
            })
        }

finishSetUp() {
    print(alreadyHaveProfile)
    if !alreadyHaveProfile {
        // finish set up, value of ALREADYHAVEPROFILE is recent and updated.
    }
}

3 个答案:

答案 0 :(得分:2)

Firebase方法observeSingleEvent(of:with:)是异步的。也就是说,该方法在回调运行之前返回。

如果你像我在这里一样在你的代码中添加print语句,我想你会看到代码中出现第二个的print语句是第一个出现在Xcode控制台中的语句。

if AccessToken.current != nil {
    fetchUserData()
    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3.5) {
        let dref = FIRDatabase.database().reference().child("CheckUsers")
        dref.observeSingleEvent(of: .value, with: { (snapshot: FIRDataSnapshot) in
            var alreadyHaveProfile = false
            let enumerator = snapshot.children
            loop: while let user = enumerator.nextObject() as? FIRDataSnapshot {
                if user.key == userID {
                    alreadyHaveProfile = true
print("1: \(alreadyHaveProfile)")
                    break loop
                }
            }
        }, withCancel: { (error: Error) in
            print(error.localizedDescription)
        })
print("2: \(alreadyHaveProfile)")

答案 1 :(得分:2)

我怀疑变量alreadyHaveProfile的状态尚未被循环内运行的逻辑设置。没有在我面前设置完整的场景设置使得很难诊断。

要考虑的几件事情;首先看起来FireBase正在执行一些数据库查询来设置变量的状态。要调试此尝试并设置一种方法来观察while循环运行的每次迭代,这样您就会知道在将print语句输出到控制台之前是否设置了变量alreadyHaveProfile的状态。

这可能有助于确定您是否处于竞争状态。

        let dref = FIRDatabase.database().reference().child("CheckUsers")
        dref.observeSingleEvent(of: .value, with: { (snapshot: FIRDataSnapshot) in
            var alreadyHaveProfile = false
            var index = 0
            if let userObjects = snapshot.children.allObjects {
                for user in userObjects {
                    if user.key == userID {
                        alreadHaveProfile = true
                    }
                    index += 1
                    print("Value: \(alreadHaveProfile) at index: \(index) in count: \(userObjects.count)")

                    if index == userObjects.count {
                        print("Loop has completed: with value of: \(alreadHaveProfile)")
                    }
                }
            }

        }, withCancel: { (error: Error) in
            print(error.localizedDescription)
        })
        print("Setting initial value: \(alreadyHaveProfile)")

要考虑的第二件事是user.key和userID根本不匹配,并且正确设置了alreadyHaveProfile值。要对此进行调试,请在循环内设置一些断点,以便在循环的每次迭代中查看user.keyuserID

答案 2 :(得分:0)

在异步块内部访问 alreadyHaveProfile 变量。

下面是代码:

if AccessToken.current != nil {
        fetchUserData()
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3.5) {
            let dref = FIRDatabase.database().reference().child("CheckUsers")
            dref.observeSingleEvent(of: .value, with: { (snapshot: FIRDataSnapshot) in
                var alreadyHaveProfile = false
                let enumerator = snapshot.children
                loop: while let user = enumerator.nextObject() as? FIRDataSnapshot {
                    if user.key == userID {
                        alreadyHaveProfile = true
                        print(alreadyHaveProfile)
                        break loop
                    }
                }
            }, withCancel: { (error: Error) in
                print(error.localizedDescription)
            })