Swift /如何使用dispatch_group与多个被称为Web服务? [英] Swift / How to use dispatch_group with multiple called web service?

查看:283
本文介绍了Swift /如何使用dispatch_group与多个被称为Web服务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 dispatch_group 来调用Firebase请求函数,并在请求完成后得到通知,以便能够使用结果。在这种情况下,我只是把一个打印语句。

pre c $ func loadStuff(){
dispatch_group_enter(group)
myFirebaseFunction(){
dispatch_group_leave(group)
}
$ b $ dispatch_group_notify(group,dispatch_get_main_queue()){
print(done)



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

let usersRef = firebase.child(likes)
usersRef.observeEventType(.Value,withBlock:{snapshot in
$ b $ if snapshot.exists(){
let sorted =(snapshot.value!.valValues as NSArray).sortedArrayUsingDescriptors([


$ b dict.append(item as!NSDictionary)
} $ {


$ b $ completionHandler()
})
}

Ť他的代码工作正常。 问题在于,在运行时,数据将被添加到Firebase数据库中。这就是为什么我必须使用 observeEventType 而不是 observeSingleEventOfType



这意味着在运行时有一个观察者,并且在数据被添加到数据库的情况下,将再次调用 myFirebaseFunction 中的块。



一旦发生这种情况,应用程序崩溃,因为已经调用 dispatch_group_leave(group)而没有 dispatch_group_enter(group) code>。
$ b $ pre $ dispatch_group_enter(group)
myFirebaseFunction(){
dispatch_group_leave(如果我将它改为}

> observeSingleEventOfType ,不会发生崩溃,但新增到Firebase的数据将不会被观察到。



最好的做法是使用 dispatch_group 与多个运行的Web服务?或者我需要做些什么来解决我的问题?帮助非常感谢。



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()
如果snapshot.exists(){
let sorted =(snapshot.value!.valValues as NSArray).sortedArrayUsingDescriptors([NSSortDescriptor(key:date,ascending:false)])

对于已排序的项目{

dict.append(item as! NSDictionary)


completionHandler

$ b $ $ $ $ $ $ $
$ b

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



传递给 dispatch_group_notify 的块只能被调用一次,即使在调用notify之后对块执行了块。正因为如此,每个对 observeEventType 的自动调用都可能在组上发生。然后在这些函数之外的任何时候,你需要等待一个加载完成,你可以调用notify。


$ b

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



方法2 - 只有第一次调用组时,有几种方法



如果你真正需要的只是第一次调用 observeEventType 来使用该组,那么一个选项是有两个版本的 myFirebaseFunction :一个很像你已经有一个和一个使用 observeSingleEventOfType 。然后,加载内容可以调用这两个函数,只传递 dispatch_group_leave 作为其中一个函数的完成:

<$ p $ ()$ $ b $ dispatch_group_enter(group)
myInitialFirebaseFunction(){
dispatch_group_leave(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 (快照)
completionHandler()
))
}

func processSnapshot(snapshot:FDataSnapshot){

如果snapshot.exists( ){
let sorted =(snapshot.value!.allValues as NSArray).sortedArrayUsingDescriptors([NSSortDescriptor(key:date,ascending:false)])

用于排序{
dict.append(item as! NSDictionary)
}
}
}



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

  
$ b $ func loadStuff(){
dispatch_group_enter(group)
shouldLeaveGroupOnProcess = true $ b $ myFirebaseFunction(){$ b $ if ifLeaveGroupOnProcess {
shouldLeaveGroupOnProcess = false
dispatch_group_leave(group)
}
}

dispatch_group_notify(group,dispatch_get_main_queue()){
print(完成)



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

let usersRef = firebase.child likes)
usersRef.observeEventType(.Value,withBlock:{snapshot in
$ b $ if if snapshot.exists(){
let sorted =(snapshot.value!.allValues as NSArray).sortedArrayUsingDescriptors([NSSortDescriptor(key:date,ascending:false)])

在排序的项目{
dict.append(item as! NSDictionary)


completionHandler

$ b $ $ $ $ $ $ $
$ b

在这里,即使在初始加载过程中对 observeEventType 进行了多次调用, leave 保证只被调用一次,不会发生崩溃。



Swift 3




PS目前我使用的是Swift 2.3,但是升级到Swift 3的计划是这样的,所以收到两者的答案是非常棒的。




dispatch在Swift 3中得到了彻底的改进(它是面向对象的),所以在两者上运行良好的代码并不是真正的东西:)



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



  • 使用 DispatchGroup

  • dispatch_group_enter 现在是实例方法输入
  • dispatch_group_leave 现在是组中的实例方法 leave

  • dispatch_group_notify 现在是组中的实例方法 notify
  • ul>

    I am using dispatch_group to call Firebase requesting functions and get notified once the request is complete to be able to work with the result then. In this scenario I've just put a print statement.

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

    This code is working fine. The problem is that, during runtime, data will be added into the Firebase Database. That is why I have to use observeEventType instead of observeSingleEventOfType.

    This means there is an observer during runtime and in case data has been added to the database, the block within myFirebaseFunction will be called again.

    Once this happens, the app crashes because dispatch_group_leave(group) has been called without dispatch_group_enter(group). As long as I got this right.

    dispatch_group_enter(group)
        myFirebaseFunction() {
             dispatch_group_leave(group)      // crash here
        }
    

    If I change it to observeSingleEventOfType, no crash occurs, but new added Data to Firebase will not be observed.

    What's the best practice to use dispatch_group with multiple run Web Services? Or what do I have to do to fix my issue? Help is very appreciated.

    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.

    解决方案

    The Problem

    As you stated, calls to dispatch_group_enter and dispatch_group_leave must be balanced. Here, you are unable to balance them because the function that performs the actual real-time fetching only calls leave.

    Method 1 - All calls on the group

    If you take no issue with myFirebaseFunction always performing its work on that dispatch group, then you could put both enter and leave inside there, perhaps with a beginHandler and 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()
       })   
    }
    

    Here, the completion handler would still be set to dispatch_group_leave by loadStuff, but there is also a begin handler that would call dispatch_group_enter and also dispatch_group_notify. The reason notify would need to be called in begin is that we need to ensure that we have already entered the group before we call notify or the notify block will execute immediately if the group is empty.

    The block you pass to dispatch_group_notify will only be called exactly once, even if blocks are performed on the group after notify has been called. Because of this, it might be safe for each automatic call to observeEventType to happen on the group. Then anytime outside of these functions that you need to wait for a load to finish, you can just call notify.

    Edit: Because notify is called each time beginHandler is called, this method would actually result in the notify block being called every time, so it might not be an ideal choice.

    Method 2 - Only first call on group, several methods

    If what you really need is for only the first call of observeEventType to use the group, then one option is to have two versions of myFirebaseFunction: one much like the one you already have and one using observeSingleEventOfType. Then load stuff could call both of those functions, only passing dispatch_group_leave as a completion to one of them:

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

    Method 3 - Only first call on group, no extra methods

    Note that because loadStuff in "Method 2" basically loads things from Firebase twice, it might not be as efficient as you'd like. In that case you could instead use a Bool to determine whether leave should be called:

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

    Here, even if multiple calls to observeEventType are made during the initial load, leave is guaranteed to be called only once and no crashed should occur.

    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.

    Dispatch has gotten a complete overhaul in Swift 3 (it is object-oriented), so code that works well on both is not really a thing :)

    But the concepts of each of the three methods above are the same. In Swift 3:

    • Create your group with one of the inits of DispatchGroup
    • dispatch_group_enter is now the instance method enter on the group
    • dispatch_group_leave is now the instance method leave on the group
    • dispatch_group_notify is now the instance method notify on the group

    这篇关于Swift /如何使用dispatch_group与多个被称为Web服务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆