一尘不染

Swift /如何将dispatch_group与多个调用的Web服务一起使用?

swift

dispatch_group用来调用Firebase请求函数,并在请求完成后得到通知,以便能够处理结果。在这种情况下,我只是打印了一条语句。

func loadStuff() {
    dispatch_group_enter(group)
        myFirebaseFunction() {
             dispatch_group_leave(group)
        }

    dispatch_group_notify(group, dispatch_get_main_queue()) {
        print("done")
    }
}

func myFirebaseFunction(completionHandler: () -> ()) {

    let usersRef = firebase.child("likes")
    usersRef.observeEventType(.Value, withBlock: { snapshot in

             if snapshot.exists() {
                   let sorted = (snapshot.value!.allValues as NSArray).sortedArrayUsingDescriptors([NSSortDescriptor(key: "date",ascending: false)])

                   for item in sorted {

                       dict.append(item as! NSDictionary)
                   }
            }
            completionHandler()
   })   
}

这段代码工作正常。 问题
在于,在运行时,数据将被添加到Firebase数据库中。这就是为什么我必须使用observeEventType而不是observeSingleEventOfType

这意味着在运行时会有一个观察者,并且如果数据已添加到数据库中,myFirebaseFunction则将再次调用其中的块。

一旦发生这种情况,该应用就会崩溃,因为dispatch_group_leave(group)没有调用dispatch_group_enter(group)。只要我说对了。

dispatch_group_enter(group)
    myFirebaseFunction() {
         dispatch_group_leave(group)      // crash here
    }

如果将其更改为observeSingleEventOfType,则不会发生崩溃,但是不会观察到新添加到Firebase的数据。

dispatch_group与多个运行的Web服务一起使用的最佳实践是什么?或者我该怎么做才能解决我的问题?非常感谢您的帮助。

PS当前,我正在使用Swift 2.3,但计划将其升级到Swift 3,因此收到能够同时解决这两个问题的答案将非常棒。


阅读 262

收藏
2020-07-07

共1个答案

一尘不染

问题

如您所述,呼叫dispatch_group_enterdispatch_group_leave必须保持平衡。在这里,您无法平衡它们,因为仅执行实际实时提取的函数会离开。

方法1-组中的所有呼叫

如果myFirebaseFunction始终在该调度组上执行其工作没有问题,则可以将进入和离开都放在其中,也许使用beginHandler和completedHandler:

func loadStuff() {
    myFirebaseFunction(beginHandler: {
        dispatch_group_enter(group)
        dispatch_group_notify(group, dispatch_get_main_queue()) {
            print("done")
        }
    }, completionHandler: { dispatch_group_leave(group) })

}

func myFirebaseFunction(beginHandler: () -> (), completionHandler: () -> ()) {

    let usersRef = firebase.child("likes")
    usersRef.observeEventType(.Value, withBlock: { snapshot in

        beginHandler()
        if snapshot.exists() {
            let sorted = (snapshot.value!.allValues as NSArray).sortedArrayUsingDescriptors([NSSortDescriptor(key: "date",ascending: false)])

            for item in sorted {

                dict.append(item as! NSDictionary)
            }
        }
        completionHandler()
   })   
}

在这里,完成处理程序仍将设置为dispatch_group_leaveby
loadStuff,但是还有一个begin处理程序将调用dispatch_group_enterand
dispatch_group_notify。需要在开始时调用notify的原因是,我们需要确保在调用notify之前已经进入了该组,否则如果该组为空,则notify块将立即执行。

即使传递给dispatch_group_notify通知的块在组上执行,即使传递给该块的块也只会被调用一次。因此,observeEventType在组上进行每次自动呼叫可能是安全的。然后,在这些功能之外需要等待加载完成的任何时间,您都可以调用notify。

编辑: 由于每次beginHandler调用notify都会被调用,因此此方法实际上会导致每次调用notify块,因此它可能不是理想的选择。

方法2-仅在组中首次调用,有几种方法

如果您真正需要的仅是第一次调用observeEventType使用该组,则一个选项是具有的两个版本myFirebaseFunction:一个与您已经拥有的版本非常相似,另一个与一起使用observeSingleEventOfType。然后加载的东西可以调用这两个函数,仅dispatch_group_leave作为完成传递给其中一个:

func loadStuff() {
    dispatch_group_enter(group)
        myInitialFirebaseFunction() {
            dispatch_group_leave(group)
        }

    dispatch_group_notify(group, dispatch_get_main_queue()) {
        print("done")
    }

    myFirebaseFunction({})
}

func myInitialFirebaseFunction(completionHandler: () -> ()) {

    let usersRef = firebase.child("likes")
    usersRef.observeSingleEventOfType(.Value, withBlock: { snapshot in
        processSnapshot(snapshot)
        completionHandler()
    })   
}

func myFirebaseFunction(completionHandler: () -> ()) {

    let usersRef = firebase.child("likes")
    usersRef.observeSingleEventOfType(.Value, withBlock: { snapshot in
        processSnapshot(snapshot)
        completionHandler()
    })   
}

func processSnapshot(snapshot: FDataSnapshot) {

    if snapshot.exists() {
        let sorted = (snapshot.value!.allValues as NSArray).sortedArrayUsingDescriptors([NSSortDescriptor(key: "date",ascending: false)])

        for item in sorted {
            dict.append(item as! NSDictionary)
        }
    }
}

方法3-仅在组上首次调用,没有其他方法

请注意,由于loadStuff在“方法2”中基本上从Firebase加载了两次,因此效率可能不如您所愿。在这种情况下,您可以改用a
Bool来确定是否应调用请假:

var shouldLeaveGroupOnProcess = false

func loadStuff() {
    dispatch_group_enter(group)
        shouldLeaveGroupOnProcess = true
        myFirebaseFunction() {
            if shouldLeaveGroupOnProcess {
                shouldLeaveGroupOnProcess = false
                dispatch_group_leave(group)
            }
        }

    dispatch_group_notify(group, dispatch_get_main_queue()) {
        print("done")
    }
}

func myFirebaseFunction(completionHandler: () -> ()) {

    let usersRef = firebase.child("likes")
    usersRef.observeEventType(.Value, withBlock: { snapshot in

        if snapshot.exists() {
            let sorted = (snapshot.value!.allValues as NSArray).sortedArrayUsingDescriptors([NSSortDescriptor(key: "date",ascending: false)])

            for item in sorted {
                dict.append(item as! NSDictionary)
            }
        }
        completionHandler()
    })   
}

在这里,即使observeEventType在初始加载期间多次调用,也leave可以保证仅被调用一次,并且不会发生崩溃。

迅捷3

PS当前,我正在使用Swift 2.3,但计划将其升级到Swift 3,因此收到能够同时解决这两个问题的答案将非常棒。

在Swift 3中Dispatch进行了全面的改进(它是面向对象的),因此在两者上都能很好地工作的代码并不是一件真正的事情:)

但是以上三种方法中每种方法的概念都是相同的。在Swift 3中

  • 使用以下其中一种方法创建小组 DispatchGroup
  • dispatch_group_enter现在enter是组上的实例方法
  • dispatch_group_leave现在leave是组上的实例方法
  • dispatch_group_notify现在notify是组上的实例方法
2020-07-07