Swift 立即访问Firebase块中的值/避免竞争条件

Swift 立即访问Firebase块中的值/避免竞争条件,swift,firebase,firebase-realtime-database,boolean,Swift,Firebase,Firebase Realtime Database,Boolean,我有一个bool“alreadyHaveProfile”,在while循环中我将其更改为TRUE,然后我打破了循环…但是布尔值仍然没有改变。有什么想法吗 if AccessToken.current != nil { fetchUserData() DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3.5) { let dref = FIRDatabase.databa

我有一个bool“alreadyHaveProfile”,在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.
    }
}

异步块内的AcessalreadyHaveProfile变量

下面是代码:

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

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

我怀疑变量
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)")

第二个要考虑的是,用户可能。密钥和用户ID根本不匹配,并且<代码> ALLRIDYHAVEPrime<代码>值被正确设置。要调试此操作,请在循环中设置一些断点,以便在循环的每个迭代中查看

user.key
userID

打印(alreadyHaveProfile)不是在同步块之前执行的吗?您可以简化该循环:
alreadyHaveProfile=snapshot.children.contains(其中:{($0 as?FIRDataSnapshot)?.key==userID
@Alexander smooth man,感谢您提供的有用信息!我还有另一个IF块,它在上面的第二个print语句之后检查ALREADYHAVEPROFILE的值……有没有办法访问更新后的值?因为我必须等到observeSingleEvent块运行之后才能接收upalreadyHaveProfile的日期真值。没有回答问题…很抱歉,你明白了,伙计…这是一个竞争条件。在循环外的print语句执行后,我的值变为真。那么,在执行查询后立即访问更新的布尔值的最佳选项是什么?很高兴我能提供帮助!我会说如果observeSingleEvent()方法有一个到闭包的完成块,这将是一个很好的开始。否则,您还可以通过检查循环是否已完成来访问
alreadHaveProfile
的值。
if index==userObjects.count{print(“具有值的已完成循环:\(alreadHaveProfile)”)
是的,你是对的。我将其从我的答案中删除。更好的选择当然是查看是否有完成块可用。我提供的例程将作为临时解决方案。你也可以查看调度组作为同步执行逻辑的选项。感谢你的帮助!我能够想出一个非常简单的解决方案绝对正确!很高兴我们能够一起为您找到解决方案!