swift - 如何在嵌套的 Firebase 异步 observeSingleEvent() 方法 Swift 中使用 DispatchGroup?

标签 swift firebase asynchronous parallel-processing grand-central-dispatch

我正在尝试遍历两个 Firebase 数据库引用,firstRefsecondRef,使用 observeSingleEvent Firebase 异步方法观察各自的值,并将值附加到两个数组 firstArraysecondArray。完成后,我想通知调度组并执行完成处理程序。但是,当调用以下方法时,这两个数组总是意外地发现 nil。我不太清楚为什么会这样,或者是否会出现这个问题,因为我嵌套了异步函数。我知道强制展开在语法上也不受欢迎,但是,在这种情况下,我希望这两个数组始终填充从数据库中检索到的数据。

func loop(inputArray: [inputType], doneLoop: @escaping (_ firstArray: [firstType],_ secondArray: [secondType]) -> Void) {

    var firstArray: [firstType]?
    var nameArray: [secondType]?

    let dispatchGroup = DispatchGroup()

    for element in inputArray {

        let firstRef = Database.database().reference(withPath: "firstPath")

            //Enter dispatch group
            dispatchGroup.enter()
            firstRef.observeSingleEvent(of: .value, with: { snapshot in

                //Storing first snapshot value

                let firstSnapshotValue = snapshot.value as! firstType

                let secondRef = Database.database().reference(withPath: "secondPath")

                secondRef.observeSingleEvent(of: .value, with: { snapshot in


                    //Storing second snapshot value
                    let secondSnapshotValue = snapshot.value as? String

                    //Append retrieved values to arrays
                    firstArray?.append(firstSnapshotValue)
                    secondArray?.append(secondSnapshotValue)
                })

            })
            //Leave dispatch group
            dispatchGroup.leave()

    }

    //Notify on main queue and execute completion handler
    dispatchGroup.notify(queue: .main) {
        doneLoop(firstArray!, secondArray!)
    }


}

我是否需要创建一个带有@escaping 完成处理程序的新函数?唯一的问题是,由于它在数组中循环,完成只会执行一次,因此只会填充一个值。

如有任何帮助,我们将不胜感激!

最佳答案

你应该同时制作firstArraynameArray:

var firstArray = [firstType]()
var nameArray = [secondType]()

leave() dispatchGroup 在最里面的回调中。并考虑将其包装在 defer{} 中,以防止将来在回调中出现任何多路径。

func loop(inputArray: [inputType], doneLoop: @escaping (_ firstArray: [firstType],_ secondArray: [secondType]) -> Void) {

  var firstArray = [firstType]()
  var nameArray = [secondType]()

  let dispatchGroup = DispatchGroup()

  for element in inputArray {

    let firstRef = Database.database().reference(withPath: "firstPath")

    //Enter dispatch group
    dispatchGroup.enter()
    firstRef.observeSingleEvent(of: .value, with: { snapshot in

      //Storing first snapshot value

      let firstSnapshotValue = snapshot.value as! firstType

      let secondRef = Database.database().reference(withPath: "secondPath")

      secondRef.observeSingleEvent(of: .value, with: { snapshot in
        //Leave dispatch group
        defer { dispatchGroup.leave() }

        //Storing second snapshot value
        let secondSnapshotValue = snapshot.value as? String

        //Append retrieved values to arrays
        firstArray.append(firstSnapshotValue)
        secondArray.append(secondSnapshotValue)
      })

    })

  }

  //Notify on main queue and execute completion handler
  dispatchGroup.notify(queue: .main) {
    doneLoop(firstArray, secondArray)
  }

}

此外,如果 secondRef 的值是独立的,请考虑取消嵌套内部 observeSingleEvent 调用(并添加额外的 enter()leave() 调用)。

并发考虑

照原样,您的代码可能/最终会丢失对 firstArraysecondArray 的写入。 Firebase 的 observeSingleEvent 执行其 with block 的速度越快,数据丢失的速度就越快。为避免这种情况,所有写入(append)都应从串行队列完成:

let queue = DispatchQueue(label: "tld.company.app.queue")
queue.async() {
  // Write
}
queue.sync() {
  // Read
}

或者,更好的是,使用 .barrier 标志保持读取并发:

let queue = DispatchQueue(label: "tld.company.app.queue", attributes: .concurrent)
queue.async(flags: .barrier) {
  // Write
}
queue.sync() {
  // Read
}

阅读更多:Create thread safe array in Swift

关于swift - 如何在嵌套的 Firebase 异步 observeSingleEvent() 方法 Swift 中使用 DispatchGroup?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57865453/

相关文章:

android - Firebase Android 获取未知 key 的值

javascript - Firebase 的云功能 : functions. pubsub.topic ('testTopic' ).onPublish 未调用

firebase - 为什么云函数 URL (Firebase) 可以公开访问?如何限制访问特定的外部调用?

javascript - (babel插件)TypeError : Path was expected to have a container

java - Spring:@Validated 和@Async 不能一起工作

javascript - 在 Node.js 中异步读写文件

ios - ViewController 不符合 Swift 上的协议(protocol)

ios - 如何添加约束以右对齐 UITableViewCell 内的图像

swift - 无法推断通用参数 'T' - Codable

php - 快速错误 : Code=3840 "Garbage at end."