问题
正如您所说,对dispatch_group_enter 和dispatch_group_leave 的调用必须平衡。在这里,您无法平衡它们,因为执行实际实时获取的函数只调用 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。在 begin 中需要调用 notify 的原因是我们需要确保在调用 notify 之前我们已经进入了 group,否则如果 group 为空,则 notify 块将立即执行。
您传递给dispatch_group_notify 的块只会被调用一次,即使在调用 notify 之后对组执行块也是如此。因此,对observeEventType 的每次自动调用在组上发生可能是安全的。然后,在您需要等待加载完成的这些功能之外的任何时候,您都可以调用 notify。
编辑:因为每次调用beginHandler时都会调用notify,所以这个方法实际上会导致每次调用notify块,所以它可能不是一个理想的选择。
方法 2 - 仅第一次调用组,几种方法
如果您真正需要的只是第一次调用observeEventType 以使用该组,那么一种选择是拥有两个版本的myFirebaseFunction:一个很像您已有的版本,一个使用observeSingleEventOfType .然后 load stuff 可以调用这两个函数,只将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,leave 也保证只被调用一次,不会发生崩溃。
斯威夫特 3
PS 目前我正在使用 Swift 2.3,但计划升级到 Swift 3,所以如果能同时获得两者的答案,那就太好了。
Dispatch 在 Swift 3 中得到了彻底的改造(它是面向对象的),所以在两者上都运行良好的代码并不是真正的事情 :)
但上述三种方法的概念都是相同的。在 Swift 3 中:
- 使用
DispatchGroup 的初始值之一创建您的组
-
dispatch_group_enter 现在是组上的实例方法 enter
-
dispatch_group_leave 现在是组上的实例方法 leave
-
dispatch_group_notify 现在是组上的实例方法 notify