如何在 Swift 3、Swift 4 及更高版本中 dispatch_sync、dispatch_async、dispatch_after 等?

Posted

技术标签:

【中文标题】如何在 Swift 3、Swift 4 及更高版本中 dispatch_sync、dispatch_async、dispatch_after 等?【英文标题】:How do I dispatch_sync, dispatch_async, dispatch_after, etc in Swift 3, Swift 4, and beyond? 【发布时间】:2016-10-14 13:23:15 【问题描述】:

我在 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 的任何其他用途...

现在我已经在 Xcode 8(测试版)中为 Swift 3 打开了我的项目,但我遇到了各种错误。他们中的一些人提供修复我的代码,但并非所有修复程序都会产生工作代码。我该怎么办?

【问题讨论】:

在这里回答:***.com/questions/37805885/… 【参考方案1】:

从一开始,Swift 就提供了一些使 ObjC 和 C 更加 Swifty 的功能,每个版本都添加了更多功能。现在,在 Swift 3 中,新的 "import as member" 功能允许具有某些样式的 C API 的框架——其中你有一个工作起来有点像类的数据类型,以及一堆与之一起工作的全局函数——行动更多像 Swift 原生 API。数据类型作为 Swift 类导入,它们相关的全局函数作为这些类的方法和属性导入,一些相关的东西,比如常量集,可以在适当的时候成为子类型。

在 Xcode 8 / Swift 3 beta 中,Apple 已应用此功能(以及其他一些功能)以使 Dispatch 框架更加 Swifty。 (还有Core Graphics)

将任何项目迁移到 Swift 3 的第一步应该是在 Xcode 8 中打开它,然后选择 Edit > Convert > To Current Swift Syntax...菜单。这将应用(经过您的审核和批准)所有重命名的 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 的 docs on background work and energy efficiency。

顺便说一句,如果您保留自己的队列来组织工作,那么现在获取队列的方法如下所示(注意 DispatchQueueAttributesOptionSet,因此您使用集合样式的文字来组合选项):

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 的界面来找到自己的方式——使用 Open Quickly 来查找 Dispatch 模块,或者在您的 Swift 项目/playground 和命令中放置一个符号(如 DispatchQueue)-单击它,然后从那里浏览模块。 (您可以在 Apple 漂亮的新 API 参考网站和 in-Xcode 文档查看器中找到 Swift Dispatch API,但看起来 C 版本的文档内容尚未移入其中。)

请参阅Migration Guide 了解更多提示。

【讨论】:

对于 Xcode 8 Beta 6,.serial 属性消失了,默认行为 - forums.developer.apple.com/message/159457#159457 自 XCode 8.1 以来需要更新。属性标签已经消失,我们可以使用 'DispatchQueue.global(qos: .background).async' 精彩的答案。真的帮助我解决了这个问题。 我不得不使用qos: 而不是attributes: class Foo 示例中不应该是myQueue.async 吗?【参考方案2】:

在 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")
)

【讨论】:

所以它不会阻塞用户界面?【参考方案3】:

在 Xcode 8 中使用:

DispatchQueue.global(qos: .userInitiated).async  

【讨论】:

【参考方案4】:

这是Swift 4 关于async 的好例子:

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

【讨论】:

hi DispatchQueue.main.async // 运行 UI 更新 在后台线程之前执行 类似 Kotlin 的协程【参考方案5】:

Swift 5.2、4 及更高版本

主队列和后台队列

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

使用异步和 sync 线程!

 background.async  //async tasks here  
 background.sync  //sync tasks here  

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

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

【讨论】:

你将如何使用同步线程而不阻塞主线程(UI)?我想在后台执行一排事情-但是这些事情必须以同步的方式一个接一个地执行。在这段时间内,用户界面应该保持响应......你会怎么做? 使用 NSOperationQueue。您的每个任务代表一个 NSOperation。参考***.com/a/19746890/5215474 拯救了我的一天!谢谢!【参考方案6】:

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
    

【讨论】:

太好了,谢谢!

以上是关于如何在 Swift 3、Swift 4 及更高版本中 dispatch_sync、dispatch_async、dispatch_after 等?的主要内容,如果未能解决你的问题,请参考以下文章

ios7及更高版本导致图像图标错误

iOS:面向 iOS 9 时使用 Swift 4

用 Swift 编写的代码可以在 iOS 7 及更早版本上运行吗? [复制]

Modern OpenGL 3.x 及更高版本如何绘制图元?

如何检查我是不是在 android 4.4 及更高版本中以编程方式打开了 gps? [复制]

如何让原生 WebSockets 在 Android 4.03 及更高版本中工作?