GCD in Swift 3

本文假设你有一定的 GCD 和 Swift 基础。

iOS 下的多线程编程技术从底层往上分别是 NSThread、NSOperation、Grand Central Dispatch(GCD),这三个技术越往后抽象度越高,使用也越简单。GCD 无论在 Objective-C 时代,还是后 OC 的 Swift 时代,都是 iOS 开发者使用最多的多线程编程技术。

GCD 是使用 C 语言编写的。多年以来,一直使用 Objective-C 的开发者们很习惯 GCD 的底层 C 语言式的代码。这种风格一直保留到 Swift 推出两年后,到了 Swift 2 。无论 Swift 迭代了多少回,GCD 依然保留其原始的 C-Style。终于,到了 2016 年,全力研发 Swift 的苹果终于想起了 GCD 这么个东西,在 WWDC 2016 推出的 Swift 3 中完全更改了 GCD 的代码风格。

回顾

在 Swift < 3 中,我们写一个 GCD 代码可能是这样的:

let queue = dispatch_queue_create("Kenneth", nil)
dispatch_async(queue) {
    print("Hello World")
}

我们创建一个串行队列,然后指派一个输出 Hello World 的异步任务给这个队列,一切看上去都很自然。然而唯一的缺点就是,这太不 Swift 了。

Swift < 3 中,Swift 标准库里面的 libdispach 是一从 C 导入的函数的集合。这也导致了在 Swift < 3 中使用 GCD 非常不符合 Swift 的语法,而且这些函数名在 Swift 中看上去也很怪异。

What's new?

我们再看看 Swift 3 中,上面的代码是如何写的:

let queue = DispatchQueue(label: "Kenneth")
queue.async {
    print("Hello World")
}

很好,这很 Swift,或者说这很 OOP。

我们先简单看一看和之前到底有何不同。

  • 在之前的 GCD 中,我们写函数的顺序是,先确定异步还是同步操作,然后把队列作为参数传入函数,然后指派一个任务闭包。在 Swift 3 中,这个顺序反了过来,我们先指定一个队列,然后再选是异步还是同步操作,这更符合面向对象的语法特点。
  • 变量名更符合 Swift API 的设计规范

仔细看看

命名方式

从上述的引言中,我们可以看到在 Swift 3 中,我们熟悉的 GCD 类型名已经发生了比较大的改变,下划线被去掉,使用了大驼峰式命名法,不过好在他们还没有脱离原来的含义,下面是一张对照表:

C Swift
dispatch_object_t DispatchObject
dispatch_queue_t DispatchQueue
dispatch_group_t DispatchGroup
dispatch_data_t DispatchData
dispatch_io_t DispatchIO
dispatch_semaphore_t DispatchSemaphore
dispatch_source_t DispatchSource
dispatch_time_t DispatchTime, DispatchWalltime
C Swift
dispatch_fd_t Int32
dispatch_block_t () -> ()
dispatch_queue_attr_t DispatchQueueAttributes

队列

主队列

主队列通常被用来做我们 App 的 UI 更新操作,在之前的版本,我们通过这个函数获取主队列:

dispatch_get_main_queue()

现在我们用这个属性:

DispatchQueue.main

全局队列

在 iOS < 8 之前,我们有四种优先级的全局队列,他们分别是:

#define DISPATCH_QUEUE_PRIORITY_HIGH         2  
#define DISPATCH_QUEUE_PRIORITY_DEFAULT      0  
#define DISPATCH_QUEUE_PRIORITY_LOW          (-2)  
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND   INT16_MIN  

在 iOS >= 8 之后,优先级的概念被苹果使用 QoS 替代了,Swift 3 中也一样。我们不再使用优先级,而是使用 QoS 来描述全局队列。简单地说,这两者之间的对应关系是这样的:

Priority DispatchQoS
DISPATCH_QUEUE_PRIORITY_HIGH .userInitiated
DISPATCH_QUEUE_PRIORITY_DEFAULT .default
DISPATCH_QUEUE_PRIORITY_LOW .utility
DISPATCH_QUEUE_PRIORITY_BACKGROUND .background

在 Swift 3 中,获取全局队列需要使用这个方法:

DispatchQueue.global(qos: DispatchQoS.*)

我们将 QoS 传入 global() 方法,实际上就像指定它的优先级。当然你也可以不指定,默认就是 default。

DispatchQueue.global()      // 等于 DispatchQueue.global(qos: .default)

创建队列

就像在前言中所举的例子,有时候我们想创建一个队列,之前我们是这么写的:

let queue = dispatch_queue_create("Kenneth", nil)

现在我们直接使用 init 函数:

let queue = DispatchQueue(label: "Kenneth")

这么写来创建一个并行队列:

let conQueue = DispatchQueue(label: "Kenneth", attributes: .concurrent)

任务

像之前所说的一样,在 Swift 3 中指派任务更加自然,也更加方便。你只需要在你所指定的队列后使用相应的方法(.sync、.async),然后使用闭包传入任务即可。

同步任务

使用 .sync 方法,例如:

let queue = DispatchQueue(label: "Kenneth")
queue.sync {
    print("Hello World")
}

Tips:请避免在主线程指定同步任务,否则你的主线程可能会锁死。

异步任务

使用 .async 方法,例如:

let queue = DispatchQueue(label: "Kenneth")
queue.async {
    print("Hello World")
}

延时执行

之前我们在 GCD 中,想要指派一个任务延时执行(比如等待一个动画),需要写的代码十分复杂。我们来看看 Swift 3 中是怎样的:

let delay = DispatchTime.now() + 3.5

DispatchQueue.main.asyncAfter(deadline: delay) {
    // 你想做啥
}

Boom!

Tips:要注意这里的单位是秒(s),如果你想更改单位的话,可以使用 DispatchTime 的 enum:

  • .seconds(Int)
  • .milliseconds(Int)
  • .microseconds(Int)
  • .nanoseconds(Int)

dispatch_once

dispatch_once 在 OC 时代是一个用来写单例的很好的工具,它保证任务只执行一次。然而在 Swift 3 中,这个函数被删除了。
在 Swift 中,好用又简洁的单例请使用 static let。

总结

随着 Swift 3 的正式发布,GCD 新型的使用方式将会被越多的人所认识。作为 iOS 开发者,我很乐于见到 GCD 新的语法更加现代化,更符合面向对象的思想,也更便于使用。下面是常用的 GCD 模板在 Swift 3 中的写法,供大家参考。

全局队列异步
DispatchQueue.global().async {
    //Something that wastes time
    DispatchQueue.main.async {
    //返回主线程
    }
}
延时操作,注意这里的单位是秒
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3.5) {
    // 你想做啥
}
创建队列同步
let queue = DispatchQueue(label: "Kenneth")
queue.sync {
    print("Hello World")
}

你可能感兴趣的:(GCD in Swift 3)