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

How do I use DispatchGroup within nested Firebase asynchronous observeSingleEvent() methods Swift?

我正在尝试遍历两个 Firebase 数据库引用 firstRefsecondRef,使用 observeSingleEvent Firebase 异步方法观察各自的值,并将这些值附加到两个数组, firstArraysecondArray。完成后,我想通知调度组并执行完成处理程序。然而,当调用下面的方法时,两个数组总是unexpectedly found 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() 调用)。

并发考虑

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

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