Swift /如何使用dispatch_group和多个被调用的Web服务?

时间:2016-09-13 08:46:32

标签: ios swift firebase firebase-realtime-database

我正在使用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,所以收到能够同时支持的答案会非常棒。

2 个答案:

答案 0 :(得分:14)

问题

如您所述,必须平衡对dispatch_group_enterdispatch_group_leave的来电。在这里,您无法平衡它们,因为执行实际实时提取的函数只会调用leave。

方法1 - 对组的所有呼叫

如果您对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_enterdispatch_group_notify的开始处理程序。需要在begin中调用notify的原因是我们需要确保在调用notify之前已经进入组,或者如果组为空则通知块将立即执行。

传递给dispatch_group_notify的块只会被调用一次,即使在调用通知后对组执行了块也是如此。因此,对observeEventType的每次自动调用在组上发生可能是安全的。然后,在这些函数之外的任何时候,您需要等待加载完成,您只需调用notify。

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

方法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来确定是否应该调用离开:

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一次,并且不会发生崩溃。

Swift 3

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