当前位置: 首页 > 面试题库 >

Swift /如何将dispatch_group与多个调用的Web服务一起使用?

颜楚青
2023-03-14
问题内容

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,因此收到能够同时解决这两个问题的答案将非常棒。


问题答案:

问题

如您所述,呼叫dispatch_group_enterdispatch_group_leave必须保持平衡。在这里,您无法平衡它们,因为仅执行实际实时提取的函数会离开。

方法1-组中的所有呼叫

如果myFirebaseFunction始终在该调度组上执行其工作没有问题,则可以将进入和离开都放在其中,也许使用beginHandler和completedHandler:

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_leaveby
loadStuff,但是还有一个begin处理程序将调用dispatch_group_enterand
dispatch_group_notify。需要在开始时调用notify的原因是,我们需要确保在调用notify之前已经进入了该组,否则如果该组为空,则notify块将立即执行。

即使传递给dispatch_group_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)
        }
    }
}

方法3-仅在组上首次调用,没有其他方法

请注意,由于loadStuff在“方法2”中基本上从Firebase加载了两次,因此效率可能不如您所愿。在这种情况下,您可以改用a
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可以保证仅被调用一次,并且不会发生崩溃。

迅捷3

PS当前,我正在使用Swift 2.3,但计划将其升级到Swift 3,因此收到能够同时解决这两个问题的答案将非常棒。

在Swift 3中Dispatch进行了全面的改进(它是面向对象的),因此在两者上都能很好地工作的代码并不是一件真正的事情:)

但是以上三种方法中每种方法的概念都是相同的。在Swift 3中

  • 使用以下其中一种方法创建小组 DispatchGroup
  • dispatch_group_enter现在enter是组上的实例方法
  • dispatch_group_leave现在leave是组上的实例方法
  • dispatch_group_notify现在notify是组上的实例方法


 类似资料:
  • 我是个很新的程序员。我在工作中的任务之一是让我们的定制工具创建草稿电子邮件,并将其放入运行该工具的用户的草稿电子邮件文件夹中。我们目前使用Exchange Server 2010(但正在迁移到Exchange Online)。 这个链接讨论了UseDefaultCredentials,但我似乎不知道如何实现它。我创建了一个名为service的ExchangeService对象,并使用以下凭据与Ex

  • 我在Scala上使用Play 2.5,我创建了一个类,可以多次调用外部web服务。 外部Web服务在某些条件下被调用并得到ok或nok的简单响应。如果可以,那么我应该更新内部对象状态,如果可以,我现在什么也不做。 这是我的类,它将String的列表作为参数,并返回要在控制器中处理的对象的Future列表。 是列表类型的列表,但我希望它只是一个简单的响应列表。 1)如何简化和纠正我的代码以获得响应列

  • JSON似乎打印到浏览器窗口,所以我不完全确定这种方法有什么问题?请谁能提供一些帮助,为什么这可能不起作用? 谢谢

  • 问题内容: 我对WireMock完全陌生。 到目前为止,我一直在使用通过SOAPUI进行模拟响应。我的用例很简单: 只需将SOAP XML请求发送到不同的端点(http:// localhost:9001 / endpoint1 ),然后返回固定的XML响应。但是MockWrire必须作为独立服务部署到专用服务器上,该服务器将充当从中提供模拟响应的中心位置。 只是想要一些开始的建议。如我所见,Wi

  • 如何编写angular服务方法参数来调用restful webservice Spring MVC,其签名中包含@RequestBody和@RequestParam。 Java代码: 角服务方法: 如果你帮助我,我将不胜感激。祝您愉快!

  • 我一直在努力寻找一些资源来帮助解释我们如何使用Web应用程序服务的文件存储。 可以将其与旧的Web角色一起使用,请参阅此处(在云服务(Web和辅助角色)中使用Azure文件服务)。 但是,“Azure Web服务”中没有OnStart()方法。