我有一个 iOS 应用程序,它在某些情况下会向服务器创建多个请求以填充屏幕。为了加快速度,我们使请求同时运行。为此,我们使用了 dispatch_group_enter、dispatch_group_leave 和 dispatch_group_notify 来确保在返回所有答案之前不会填充屏幕:
performRequest1() // calls dispatch_group_enter before sending the request and dispatch_group_leave when receiving the response
performRequest2() // calls dispatch_group_enter before sending the request and dispatch_group_leave when receiving the response
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue()) {
populateScreen()
}
当用户在我们收到两个服务器请求的响应之前按下后退按钮时,就会出现我们的问题。在这种情况下,我们有一个设置可以自动取消对服务器的请求 (1),因此不会调用我的成功/失败 block (2)。因此 dispatch_group_leave 永远不会被调用。我认为这很好(我不希望我的 populateScreen() 方法被调用)。但是iOS好像有a bug这可以防止信号量在返回到初始状态之前被释放 (3)。换句话说,我被迫调用 dispatch_group_enter 的次数与 dispatch_group_leave 的次数完全相同。即使在我只想跳过并释放所有内容的情况下。
由于将“取消”操作发送到执行上述代码的 View Controller (4) 将是对我的应用程序的重大改造,因此我想听听是否有人有更好的替代方案来等待多个请求已完成加载?或者有没有更好的方法来使用 dispatch_group_enter/leave?
编辑
澄清几点(回应@Rob Napier 的回答):
(1)取消是使用AFNetworkings内置的AFHTTPRequestOperation.cancel()函数实现的
(2) AFNetworking 确实 在发送取消操作时当然会调用失败 block 。但是在我处理的遗留代码中,有一个网络层处理取消操作,因此不会将失败发送到执行请求的类。正如@Rob Napier 建议的那样,这很可能是错误的根源,但是在大型应用程序中改变这种架构设计需要一点勇气...... :-|因此,我希望有一个解决方案,当 viewController 被释放时,我可以“中止”等待的 populateScreen() 调用。
(3) 我知道这篇文章指的是一些不同的东西。然而,我们应用程序中的崩溃导致 Xcode 停止在与该帖子中的代码完全相同的点:
0x110bab17a <+61>: jne 0x110bab19c ; <+95>
0x110bab17c <+63>: leaq 0x189d0(%rip), %rcx ; "BUG IN CLIENT OF LIBDISPATCH: Use-after-free of dispatch_semaphore_t"
0x110bab183 <+70>: movq %rcx, 0x23316(%rip) ; gCRAnnotations + 8
0x110bab18a <+77>: ud2
0x110bab18c <+79>: leaq 0x18972(%rip), %rcx ; "BUG IN CLIENT OF LIBDISPATCH: Semaphore/group object deallocated while in use"
0x110bab193 <+86>: movq %rcx, 0x23306(%rip) ; gCRAnnotations + 8
-> 0x110bab19a <+93>: ud2
好吧,这可能不是 iOS 中的错误,而只是一个非常烦人的设计决定 ;-)
(4) 这是一个很大的简化,没有布置我们应用程序的整个架构。更准确地说:我们有一个服务层,它执行网络请求并创建返回给 View Controller 的模型:-)
首先,这不是错误。您提供的链接正在讨论 dispatch_semaphore
,这是相关但不同的。在那个线程中,他们注意到即使 that 也不是错误。文档很清楚:“对 [dispatch_group_enter] 的调用必须与对 dispatch_group_leave 的调用相平衡。”这是预期的行为。
In this case we have a setup that automatically cancels the request to the server, and my success/failure blocks are therefore not called.
这是错误的。无论您在这里所说的“取消”是什么意思,都应该生成失败条件,从而导致调用失败 block 。例如,如果您在 NSURLSessionTask
上调用 cancel
,则委托(delegate)将收到错误消息。您的 performRequest()
也需要这样做。你是如何实现“取消”的?
这意味着 populateScreen()
将在取消时被调用。这应该没问题,因为您的系统必须已经能够处理请求错误,而取消只是另一种错误。
作为一个单独的问题,如果上面的代码在 View Controller 中,它可能是在错误的地方。 View Controller 不应进行网络调用并等待结果。他们应该只观察他们的模型。每当模型改变时,它们就应该改变。他们应该将用户的请求传递给模型,并让模型与网络对话。在这种特殊情况下,这可能只是将问题从 View Controller 转移到模型,因此可能不会改变太多。但这意味着 View Controller 应该能够在取消完成之前解除分配。这很重要,因为 View Controller 在弹出后永远不必保留。
关于ios - 取消 dispatch_group_notify,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33632124/
欢迎光临 OStack程序员社区-中国程序员成长平台 (https://ostack.cn/) | Powered by Discuz! X3.4 |