我正在使用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服务一起使用的最佳做法是什么?或者我该怎么做才能解决我的问题?非常感谢帮助。
答案 0 :(得分:14)
如您所述,必须平衡对dispatch_group_enter
和dispatch_group_leave
的来电。在这里,您无法平衡它们,因为执行实际实时提取的函数只会调用leave。
如果您对myFirebaseFunction
总是在该调度组上执行其工作没有任何问题,那么您可以将enter和leave放在那里,可能使用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()
})
}
此处,完成处理程序仍将dispatch_group_leave
设置为loadStuff
,但还有一个可以调用dispatch_group_enter
和dispatch_group_notify
的开始处理程序。需要在begin中调用notify的原因是我们需要确保在调用notify之前已经进入组,或者如果组为空则通知块将立即执行。
传递给dispatch_group_notify
的块只会被调用一次,即使在调用通知后对组执行了块也是如此。因此,对observeEventType
的每次自动调用在组上发生可能是安全的。然后,在这些函数之外的任何时候,您需要等待加载完成,您只需调用notify。
编辑:因为每次调用beginHandler
时都会调用notify,所以此方法实际上会导致每次调用通知块,因此它可能不是理想的选择。 / p>
如果你真正需要的只是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, 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
一次,并且不会发生崩溃。
PS目前我正在使用Swift 2.3,但计划升级到Swift 3,因此获得能够同时兼容的答案会非常棒。
Disift在Swift 3中得到了彻底的改革(它是面向对象的),因此在两者上运行良好的代码并不是真的:)
但上述三种方法中的每种方法的概念都是相同的。在Swift 3中:
DispatchGroup
dispatch_group_enter
现在是组enter
dispatch_group_leave
现在是组leave
dispatch_group_notify
现在是组notify
答案 1 :(得分:0)
var group : dispatch_group_t?
//在你的init函数中..或者......在你的viewDidLoad
中group = dispatch_group_create()
//添加功能
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")
}
}
//你的职能
func myFirebaseFunction(completionHandler: () -> ()) {
completionHandler()
}
func newFirebasedFunction(completionHandler: () -> ()){
completionHandler()
}