Swift/如何将 dispatch_group 与多个被调用的 Web 服务一起使用? [英] Swift / How to use dispatch_group with multiple called web service?

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

问题描述

我正在使用 dispatch_group 调用 Firebase 请求函数,并在请求完成后得到通知,以便能够处理结果.在这种情况下,我只是放置了一个打印语句.

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

此代码运行良好.问题是,在运行时,数据会被添加到 Firebase 数据库中.这就是为什么我必须使用 observeEventType 而不是 observeSingleEventOfType.

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.

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

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.

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

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
    }

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

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

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

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 目前我使用的是 Swift 2.3,但计划升级到 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_group_enterdispatch_group_leave 的调用必须平衡.在这里,您无法平衡它们,因为执行实际实时获取的函数仅调用离开.

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.

如果您对 myFirebaseFunction 始终在该调度组上执行其工作没有异议,那么您可以将 enter 和 leave 放在那里,也许使用 beginHandler 和 completionHandler:

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

在这里,完成处理程序仍然会被 loadStuff 设置为 dispatch_group_leave,但也有一个开始处理程序会调用 dispatch_group_enter 和还有dispatch_group_notify.需要在begin中调用notify的原因是我们需要确保在调用notify之前已经进入了该组,否则如果该组为空,则notify块将立即执行.

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.

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

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.

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

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.

如果您真正需要的只是第一次调用 observeEventType 以使用该组,那么一个选择是拥有两个版本的 myFirebaseFunction:一个很像一个您已经拥有,另一个使用 observeSingleEventOfType.然后加载东西可以调用这两个函数,只将 dispatch_group_leave 作为完成传递给其中一个:

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

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

请注意,由于方法 2"中的 loadStuff 基本上从 Firebase 加载了两次内容,因此它可能没有您希望的那么高效.在这种情况下,您可以改为使用 Bool 来确定是否应调用 leave:

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

这里,即使在初始加载时多次调用 observeEventTypeleave 也保证只调用一次,不会发生崩溃.

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.

PS 目前我使用的是 Swift 2.3,但计划升级到 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 在 Swift 3 中得到了彻底的改进(它是面向对象的),所以在这两者上都运行良好的代码并不是真正的东西:)

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

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

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

  • 使用 DispatchGroup
  • 的其中一个初始化创建您的组
  • dispatch_group_enter 现在是组上的实例方法 enter
  • dispatch_group_leave 现在是组上的实例方法 leave
  • dispatch_group_notify 现在是组上的实例方法 notify
  • 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天全站免登陆