【问题标题】:Swift / How to use dispatch_group with multiple called web service?Swift / 如何将 dispatch_group 与多个调用的 Web 服务一起使用?
【发布时间】:2017-01-20 19:18:52
【问题描述】:

我正在使用 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_enter(group) 的情况下调用了 dispatch_group_leave(group)。只要我做对了。

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

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

dispatch_group 与多个运行的Web 服务一起使用的最佳做法是什么?或者我该怎么做才能解决我的问题?非常感谢您的帮助。

PS 目前我正在使用 Swift 2.3,但计划升级到 Swift 3,所以如果能同时获得两者的答案,那就太好了。

【问题讨论】:

    标签: ios swift firebase firebase-realtime-database


    【解决方案1】:

    问题

    正如您所说,对dispatch_group_enterdispatch_group_leave 的调用必须平衡。在这里,您无法平衡它们,因为执行实际实时获取的函数只调用 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()
            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_enterdispatch_group_notify。在 begin 中需要调用 notify 的原因是我们需要确保在调用 notify 之前我们已经进入了 group,否则如果 group 为空,则 notify 块将立即执行。

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

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

    方法 2 - 仅第一次调用组,几种方法

    如果您真正需要的只是第一次调用observeEventType 以使用该组,那么一种选择是拥有两个版本的myFirebaseFunction:一个很像您已有的版本,一个使用observeSingleEventOfType .然后 load stuff 可以调用这两个函数,只将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 来确定是否应该调用 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()
        })   
    }
    

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

    斯威夫特 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

    【讨论】:

    • 天哪。非常感谢您为回答所付出的所有努力和时间。会尽快检查的
    • @DavidSeek 希望对您有所帮助!
    【解决方案2】:
    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()
    }
    

    【讨论】:

    • 这与我的逻辑有何不同?一旦观察者收到东西,它就会崩溃,并且该函数将再次被调用,因为没有调度连接,而是离开与否?
    猜你喜欢
    • 2015-12-14
    • 2012-10-15
    • 2017-02-05
    • 1970-01-01
    • 1970-01-01
    • 2019-03-05
    • 1970-01-01
    • 2021-06-18
    • 2020-04-29
    相关资源
    最近更新 更多