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_enter
和dispatch_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_enter
和dispatch_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() }