在Swift 3,Swift 4及更高版本中,我如何使用dispatch_sync,dispatch_async,dispatch_after等?


243

我在Swift 2.x(甚至1.x)项目中有很多代码,如下所示:

// Move to a background thread to do some long running work
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    let image = self.loadOrGenerateAnImage()
    // Bounce back to the main thread to update the UI
    dispatch_async(dispatch_get_main_queue()) {
        self.imageView.image = image
    }
}

或类似这样的东西来延迟执行:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
    print("test")
}

或Grand Central Dispatch API的所有其他各种用途...

既然我已经在Swift 3的Xcode 8(测试版)中打开了我的项目,就会遇到各种各样的错误。其中一些提供了修复我的代码的功能,但并非所有修复程序都能产生有效的代码。我该怎么办?


Answers:


343

从一开始,Swift就提供了一些使ObjC和C更加Swifty的功能,并为每个版本增加了更多功能。现在,在Swift 3中,新的“作为成员导入”功能使具有某些样式的C API的框架(其中您具有类似类的数据类型以及要使用的一系列全局函数)行为更像Swift原生API。数据类型作为Swift类导入,它们的相关全局函数作为这些类上的方法和属性导入,并且某些相关的东西(如常量集)可以在适当的时候成为子类型。

在Xcode 8 / Swift 3 beta中,Apple应用了此功能(以及其他一些功能)来使Dispatch框架更加迅速。(还有Core Graphics。)如果您一直在关注Swift开源工作,这不是新闻,但现在是Xcode的第一次。

将任何项目移动到Swift 3的第一步应该是在Xcode 8中将其打开,然后在菜单中选择“ 编辑”>“转换”>“到当前Swift语法... ”。这将适用于所有重命名的API所需的所有更改(需要您的审核和批准),以及其他更改。(通常,一行代码会一次受到多个更改的影响,因此,对错误修复进行响应-单独对其进行处理可能无法正确处理所有问题。)

结果是,用于将工作弹跳到背景和背景的通用模式现在看起来像这样:

// Move to a background thread to do some long running work
DispatchQueue.global(qos: .userInitiated).async {
    let image = self.loadOrGenerateAnImage()
    // Bounce back to the main thread to update the UI
    DispatchQueue.main.async {
        self.imageView.image = image
    }
}

请注意,我们使用的.userInitiated不是旧DISPATCH_QUEUE_PRIORITY常量之一。服务质量(QoS)说明符是在OS X 10.10 / iOS 8.0中引入的,它为系统确定工作的优先级和弃用旧优先级说明符提供了更清晰的方法。有关详细信息,请参阅Apple的有关后台工作和能源效率文档

顺便说一句,如果您要保留自己的队列来组织工作,那么现在就可以像这样(注意,它DispatchQueueAttributes是一个OptionSet,因此您可以使用集合样式的文字来组合选项):

class Foo { 
    let queue = DispatchQueue(label: "com.example.my-serial-queue",
                           attributes: [.serial, .qosUtility])
    func doStuff() {
        queue.async {
            print("Hello World")
        }
    }
}

以后要用dispatch_after做工作吗?这也是队列中的一种方法,它需要一个DispatchTime,其中包含用于各种数字类型的运算符,因此您可以添加整秒或小数秒:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { // in half a second...
    print("Are we there yet?")
}

您可以通过在Xcode 8中打开新的Dispatch API的接口来找到自己的出路-使用“快速打开”找到Dispatch模块,或者DispatchQueue在您的Swift项目/操场上放一个符号(如)并单击鼠标,然后在其中浏览从那里的模块。(您可以在Apple新颖的API参考网站和In-Xcode文档查看器中找到Swift Dispatch API,但看来C版本的文档内容尚未移入其中。)

有关更多提示,请参见《迁移指南》


3
至于Xcode 8 Beta 6,.serial属性消失了,并且是默认行为-forums.developer.apple.com/message/159457#159457
hyouuu

6
自XCode 8.1以来,这需要更新。属性标签已消失,可以使用'DispatchQueue.global(qos:.background).async'代替
Mike M

2
很棒的答案。确实帮助我解决了这个问题。
Mohsin Khubaib Ahmed

我不得不使用qos:,而不是attributes:
伊斯兰教问:

那不应该myQueue.async {class Foo示例中吗?
vacawama

142

在Xcode 8 beta 4中不起作用...

用:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
    print("Are we there yet?")
}

两种异步方式:

DispatchQueue.main.async {
    print("Async1")
}

DispatchQueue.main.async( execute: {
    print("Async2")
})

所以它不会阻止UI?
user25

72

这是一个很好的例子,Swift 4关于async

DispatchQueue.global(qos: .background).async {
    // Background Thread
    DispatchQueue.main.async {
        // Run UI Updates or call completion block
    }
}

嗨DispatchQueue.main.async {//运行UI更新}在后台线程之前执行
Uma Achanta

与Kotlin的协程类似
15:48 user25

40

在Xcode 8中使用:

DispatchQueue.global(qos: .userInitiated).async { }

26

Swift 5.2、4及更高版本

主队列和后台队列

let main = DispatchQueue.main
let background = DispatchQueue.global()
let helper = DispatchQueue(label: "another_thread") 

使用异步和同步线程!

 background.async { //async tasks here } 
 background.sync { //sync tasks here } 

异步线程将与主线程一起工作。

同步线程将在执行时阻塞主线程。


1
以及如何使用同步线程而不阻塞主线程(UI)?我想在后台执行一行事情-但是这些事情必须以同步方式一个接一个地执行。在此期间,UI应保持响应状态。...您将如何做?
iKK

使用NSOperationQueue。您的每个任务代表一个NSOperation。请参阅stackoverflow.com/a/19746890/5215474
Saranjith,

12

Swift 4.1和5。我们在代码的许多地方使用队列。因此,我创建了具有所有队列的Threads类。如果您不想使用Threads类,则可以从类方法中复制所需的队列代码。

class Threads {

  static let concurrentQueue = DispatchQueue(label: "AppNameConcurrentQueue", attributes: .concurrent)
  static let serialQueue = DispatchQueue(label: "AppNameSerialQueue")

  // Main Queue
  class func performTaskInMainQueue(task: @escaping ()->()) {
    DispatchQueue.main.async {
      task()
    }
  }

  // Background Queue
  class func performTaskInBackground(task:@escaping () throws -> ()) {
    DispatchQueue.global(qos: .background).async {
      do {
        try task()
      } catch let error as NSError {
        print("error in background thread:\(error.localizedDescription)")
      }
    }
  }

  // Concurrent Queue
  class func perfromTaskInConcurrentQueue(task:@escaping () throws -> ()) {
    concurrentQueue.async {
      do {
        try task()
      } catch let error as NSError {
        print("error in Concurrent Queue:\(error.localizedDescription)")
      }
    }
  }

  // Serial Queue
  class func perfromTaskInSerialQueue(task:@escaping () throws -> ()) {
    serialQueue.async {
      do {
        try task()
      } catch let error as NSError {
        print("error in Serial Queue:\(error.localizedDescription)")
      }
    }
  }

  // Perform task afterDelay
  class func performTaskAfterDealy(_ timeInteval: TimeInterval, _ task:@escaping () -> ()) {
    DispatchQueue.main.asyncAfter(deadline: (.now() + timeInteval)) {
      task()
    }
  }
}

显示使用主队列的示例。

override func viewDidLoad() {
    super.viewDidLoad()
     Threads.performTaskInMainQueue {
        //Update UI
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.