ios - 取消dispatch_group_notify

标签 ios grand-central-dispatch

我有一个 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,这是相关但不同的。在该线程中,他们指出即使也不是一个错误。文档很清楚:“对 [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/

相关文章:

javascript - Android 上的 IFA 等效项以及如何从浏览器/javascript 访问它

ios - Swift 3 转换

swift - 大中央调度

multithreading - 将某些任务分派(dispatch)到 "background thread"并在完成后在主线程上执行任务的最佳方法是什么?

objective-c - 性能测试 : sem_t v. s。 dispatch_semaphore_t 和 pthread_once_t 对比dispatch_once_t

ios - 访问转储 header 的实例变量(iOS)

ios - UILabel 属性文本在开始新行之前不会填满整行

ios - 多对一

具有 dispatch_async/asynchronous 方法的 iOS Singleton API 管理器 : what's the implication?

iphone - 不能再使用带有 XCode 的 iPhone 进行测试,iPhone OS 是较新的版本