理解为什么我需要调度回主线程

时间:2016-04-01 23:41:47

标签: ios multithreading

我只想清理一些对我来说有点不清楚的事情。请考虑以下以异步方式执行闭包的代码:

func fetchImage(completion: UIImage? -> ()) {
  dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)) {
    // fetch the image data over the internet

    // ... assume I got the data
    let image = UIImage(data: data)
    dispatch_async(dispatch_get_main_queue()) {
      completion(image)
    }
  }
}

根据我的理解,我们需要调度回主线程的原因是因为否则需要更长的时间来调用完成闭包以返回图像。

然而,我觉得透视有点俗气。例如,我还想创建一个isLoading属性,用于防止多个网络呼叫同时发生:

func fetchImage(completion: UIImage? -> ()) {
  // if isLoading is true, then don't continue getting the image because I want only 1 network operation to be running at 1 time.
  if isLoading { 
    completion(nil)
    return 
  }

  isLoading = true
  dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)) {
    let image = UIImage(data: data)

    // image creation is complete. Set isLoading to false to allow new fetches
    self.isLoading = false
    dispatch_async(dispatch_get_main_queue()) {
      completion(image)
    }
  }
}

对于上面的代码片段,我的问题是 - 我应该将self.isLoading = false放在主队列的调度块中吗?或者它是无关紧要的?

所有建议都表示赞赏!

2 个答案:

答案 0 :(得分:1)

这不是"否则需要更长时间",必须在主队列上执行对UI 的所有更新,以防止可能发生的损坏从并发更新到自动布局环境或其他不是线程安全的UI数据结构。

在iOS的早期版本中,更新主线程上的UI的常见副作用是出现升级的延迟,但是从iOS 9开始,您将获得异常。

就您的问题而言,您的代码最好始终如一。即总是在主队列上调度完成处理程序或从不执行此操作。这将允许正在编写完成块的程序员知道他们是否需要发送UI更新。

加载完成后,最好将isLoading设置为false,因此在dispatch_async之外最好。

鉴于您的函数正在检索UIImage,调用者很可能会更新用户界面,因此它很可能“很好”。在主线程上调度完成处理程序。

答案 1 :(得分:0)

要在后台从互联网上获取图片,您只需要执行异步请求,而不需要像现在这样在后台队列中执行此操作。

在主线程上,你基本上需要做所有关于UI操作的事情,因为它总是在主线程上运行。这是重要的部分。

因此,请求完成块(您将用于获取图像的块)在后台执行(因为它是异步的)并且在块中,您需要获取主线程来设置例如,UIImageView的图像。 据我所知,除了与UI元素直接相关的其他属性不需要在主线程上,我从来没有遇到过这样的问题。