ios - 同步网络调用在启动时阻止其他调用

标签 ios multithreading swift afnetworking

我有一个应用程序,它对 REST 服务执行数百个不同的网络调用(HTTP GET 请求)。调用是从应用程序的每个页面完成的,而且调用次数很多。但要求在任何其他网络请求发生之前必须完成两个请求(在启动或唤醒时)。这两个请求的结果是一些配置数据,在所有其他后续请求之前需要这些配置数据。 (这个要求有很多原因)

我有一个用于所有 GET 请求的中央方法。它使用 AFNetworking 和(当然)异步处理程序:

func GET(path: String, var parameters: Dictionary<String, String>? = nil) -> Future<AnyObject!> {
    let promise = Promise<AnyObject!>()

    manager.GET(path, parameters: parameters, success: { (task: NSURLSessionDataTask!, response: AnyObject!) -> Void in
        // some processing...
        promise.success(response)

    }) { (task: NSURLSessionDataTask!, error: NSError!) -> Void in
        // some failure handling...
        promise.failure(error)
    }

    return promise.future
}

现在的问题是 - 如何执行前两个调用并阻止所有其他调用,直到这两个调用成功?明显的解决方案是使用信号量来阻塞线程(不是主线程!),直到这两个调用成功到达,但如果可能的话我想避免这种解决方案。 (因为死锁、竞争条件、如何进行错误处理等......通常的嫌疑)

那么有没有更好的解决方案呢?

同步顺序基本上必须是:

  1. 第一次通话
  2. 等待第一个调用的成功响应
  3. 第二次通话
  4. 等待第二次调用或成功响应
  5. 允许以任何顺序进行所有其他调用(异步)

我无法在应用程序的上层执行此逻辑,因为 GET 请求可能来自应用程序的每个部分,因此我需要重写所有内容。我想在这个单个 GET 请求上集中完成此操作。

也许我已经使用的 Promise/Future 模式也是可能的,欢迎任何提示。

谢谢。

最佳答案

有几种方法可以解决此类问题:

  • 您可以使用信号量或调度组来使用 GCD(如 the answer you linked to 所示)。
  • 您可以将异步 NSOperation 自定义子类与 NSOperationQueue 结合使用。
  • 或者您可以使用 Promise。

但我通常建议您选择这三个之一,但不要尝试使用现有的 futures/promises 代码引入调度/操作队列模式。人们会建议使用调度/操作队列方法,只是因为这是解决此类问题的一般方法(使用 future 尚未获得普遍接受,并且存在竞争的 Promise/future 库)。但 Promise/Futures 的设计正是为了解决这个问题,如果你正在使用它,就坚持下去吧。

关于调度/操作方法的具体细节,我可以解释为什么我不喜欢 GCD 方法,并尝试说服您使用 NSOperationQueue 方法,但如果您是使用 promise / future 。

不过,这里的挑战是您似乎使用的是旧版本的 Thomvis/BrightFutures。当前版本采用两种通用类型。所以我下面的代码可能不适合你。但我会将其作为建议提供,因为它可能具有说明性。

例如,假设您有一个 loginURL(第一个请求),以及您想要在之后执行的第二个请求,但随后有一个数组 urls 您希望彼此同时运行,但仅限于前两个请求完成后。使用 BrightFutures 3.2.2,它可能看起来像:

GET(loginURL).flatMap { responseObject -> Future<AnyObject!, NSError> in
    return self.GET(secondRequestURL)
}.flatMap { responseObject -> Future<Int, NSError> in
    return urls.map { self.GET($0) }.fold(0) { sum, _ in sum + 1 }
}.onSuccess { count in
    print("\(count) concurrent requests completed successfully")
}.onFailure { error in
    print("not successful: \(error)")
}

从您的代码片段来看,您必须使用旧版本的 BrightFutures,因此上面的内容可能无法按编写的方式工作,但希望这说明了基本思想。使用 BrightFutures 的功能来管理这些异步任务并控制哪些任务是顺序完成的,哪些是并发完成的。

关于ios - 同步网络调用在启动时阻止其他调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35045829/

相关文章:

ios - 如何在 Ionic 应用程序中添加后台服务

iphone - 如何让 iphone 像桌面浏览器一样浏览网页?

ios - 无法在 iOS 可执行文件中获取 Mach-O 文本段大小

c++ - 自Windows 10 20H1起,多个具有单独线程的窗口停止工作

c++ - 如何根据固定的循环频率正确协调线程?

c - 虽然从 sigwaitinfo 函数返回带有偏移量的 si_addr 导致段错误

ios - UITextDocumentProxy adjustTextPositionByCharacterOffset 问题

swift - (Swift) 需要帮助在 View Controller 之间滑动

ios - 如何将 UiSearchBar 的背景颜色更改为黑色 -SWIFT

swift - 在另一个 TableView 上调用 dismiss() 后刷新 TableView 数据