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

Dav*_*eek 12 ios firebase swift firebase-realtime-database

dispatch_group用来调用Firebase请求函数,并在请求完成后得到通知,以便能够处理结果.在这种情况下,我刚刚发布了一个print语句.

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()
   })   
}
Run Code Online (Sandbox Code Playgroud)

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

这意味着在运行时期间有一个观察者,如果数据已添加到数据库中,myFirebaseFunction则将再次调用其中的块.

一旦发生这种情况,应用程序崩溃,因为dispatch_group_leave(group)没有调用dispatch_group_enter(group).只要我做对了.

dispatch_group_enter(group)
    myFirebaseFunction() {
         dispatch_group_leave(group)      // crash here
    }
Run Code Online (Sandbox Code Playgroud)

如果我将其更改为observeSingleEventOfType,则不会发生崩溃,但不会观察到新添加的数据到Firebase.

使用dispatch_group多个运行Web服务的最佳实践是什么?或者我该怎么做才能解决我的问题?非常感谢帮助.

PS目前我正在使用Swift 2.3,但计划升级到Swift 3,所以收到两者兼有的答案会非常棒.

Mat*_*man 16

问题

如你所说,呼叫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()
   })   
}
Run Code Online (Sandbox Code Playgroud)

在这里,完成处理器仍然会被设置为dispatch_group_leave通过loadStuff,但也有开始处理程序,它会叫dispatch_group_enterdispatch_group_notify.需要在begin中调用notify的原因是我们需要确保在调用notify之前已经进入组,或者如果组为空则通知块将立即执行.

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

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

方法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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

方法3 - 只有第一次调用组,没有额外的方法

请注意,因为loadStuff在"方法2"中基本上从Firebase加载了两次,所以它可能不如您所希望的那样高效.在这种情况下,您可以使用a 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()
    })   
}
Run Code Online (Sandbox Code Playgroud)

这里,即使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是组上的实例方法