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(组)已调用,而没有dispatch_group_enter(组).只要我有这个权利.
dispatch_group_enter(group) myFirebaseFunction() { dispatch_group_leave(group) // crash here }
如果我将其更改为observeSingleEventOfType,则不会发生崩溃,但是新添加到Firebase的数据将不会被观察到.
在多个运行的Web服务中使用dispatch_group的最佳做法是什么?或者我需要做些什么来解决我的问题?帮助非常感激.
PS目前我正在使用Swift 2.3,但是升级到Swift 3是有计划的,所以收到一个能够兼容的答案将非常棒.
解决方法
正如你所说,调用dispatch_group_enter和dispatch_group_leave必须平衡.在这里,您无法平衡它们,因为执行实时实时获取呼叫的功能离开.
方法1 – 组上的所有呼叫
如果没有问题,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块将立即执行.
传递给dispatch_group_notify的块只会被精确调用一次,即使在通知被调用后在组上执行了块.因此,每次自动调用observeEventType可能会对组发生安全.然后在这些功能之外的任何时候,您需要等待加载完成,您只需调用notify即可.
编辑:因为每次调用beginHandler时都会调用notify,所以这个方法实际上会导致每次调用notify块,所以可能不是一个理想的选择.
如果您真正需要的仅仅是第一次调用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) } } }
请注意,因为“方法2”中的loadStuff基本上是从Firebase两次加载东西,它可能没有你想要的那么高效.在这种情况下,您可以使用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,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 Currently I’m using Swift 2.3,but an upgrade to Swift 3 is planned,so it would be very awesome to receive an answer capable for both.
调度已经在Swift 3中进行了彻底的检修(它是面向对象的),所以在两者之间运行良好的代码并不是一件很重要的事情:)
但是上述三种方法的概念是一样的.在Swift 3:
>使用DispatchGroup的其中一个创建您的组> dispatch_group_enter现在是在组上输入的实例方法> dispatch_group_leave现在是组上的实例方法> dispatch_group_notify现在是组上的实例方法通知