Swift /如何使用dispatch_group与多个被称为Web服务?

我正在使用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的计划是这样的,所以接收两者的答案将会非常棒。

问题

如您所述,调用dispatch_group_enterdispatch_group_leave必须是平衡的。 在这里,您无法平衡它们,因为执行实际获取的函数只能调用离开。

方法1 – 组中的所有呼叫

如果您对myFirebaseFunction始终在该调度组执行任何myFirebaseFunction都没有问题,那么您可以使用beginHandler和completionHandler进入和离开:

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

在这里,完成处理程序仍然会被loadStuff设置为dispatch_group_leave ,但是也有一个开始处理程序,它将调用dispatch_group_enterdispatch_group_notify 。 需要先通知notify的原因是我们需要确保在我们调用notify之前已经进入了这个组,否则如果这个组是空的,notify块将立即执行。

传递给dispatch_group_notify的块只能被调用一次,即使在调用notify之后对块执行了块。 正因为如此,每个自动调用observeEventType都可能是安全的。 然后,除了这些需要等待加载完成的function外,您可以直接调用notify。

编辑:因为每次调用beginHandler都会调用notify,所以这个方法实际上会导致每次调用notify块,所以它可能不是一个理想的select。

方法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 – 只有先调用组,不需要额外的方法

请注意,因为“方法2”中的loadStuff基本上从Firebase加载了两次,所以效率可能不如您想要的高。 在这种情况下,您可以使用Bool来确定是否应该调用leave:

 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也只能保证一次调用,不会发生崩溃。

Swift 3

PS目前我使用的是Swift 2.3,但是升级到Swift 3的计划是这样的,所以如果能够得到两者的答案,那将是非常棒的。

在Swift 3(它是面向对象的)调度已经得到了彻底的检修,所以在两者上运行良好的代码并不是真正的事情:)

但是,上述三种方法的概念都是一样的。 在Swift 3:

  • DispatchGroup一个inits创build你的组
  • dispatch_group_enter现在是组中enter的实例方法
  • dispatch_group_leave现在是组中的实例方法
  • dispatch_group_notify现在是组中的实例方法notify
 var group : dispatch_group_t? 

//在你的init函数中,或者在你的viewDidLoad中

 group = dispatch_group_create() 

//添加function

 func leaveDispatchGroup(){ dispatch_group_leave(group!) } func joinDispatchGroup(){ dispatch_group_enter(group!) } 

//执行你的操作

 func makeAllAPICalls() { self.joinDispatchGroup() //API Operation self.myFirebaseFunction() { //leave group on completion self.leaveDispatchGroup() } self.joinDispatchGroup() //API Operation self.newFirebasedFunction() { //leave group on completion self.leaveDispatchGroup() } //this will call once your all operation will leave the group, so add this after join all operations dispatch_group_notify(group!, dispatch_get_main_queue()) { () -> Void in //dispatch group notify... print("done") } } 

//你的function

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