【iOS】Swift系列三十一 - 多线程

多线程在Swift中也是首先使用GCD。

一、异步

1.1. GCD开启异步线程

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

print("主线程", Thread.current)

DispatchQueue.global().async {
print("子线程", Thread.current)
DispatchQueue.main.async {
print("回到主线程", Thread.current)
}
}
}
}
/*
输出:
主线程 <NSThread: 0x600000bdc6c0>{number = 1, name = main}
子线程 <NSThread: 0x600000bb4c80>{number = 6, name = (null)}
回到主线程 <NSThread: 0x600000bdc6c0>{number = 1, name = main}
*/

提示:之前OC中很多类都是NS前缀,但是Swift中和OC对等的类名大部分把NS去掉了。

1.2. GCD任务-DispatchWorkItem

DispatchWorkItem是定义任务的,任务完成后可以通过notify通知其他线程(一般是主线程)做其他任务。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 定义任务
public typealias Task = () -> Void

public struct Async {
// 异步线程1:传入子线程任务
public static func async (_ task: @escaping Task) {
_async(task)
}

// 异步线程2:分别传入子线程和主线程的任务
public static func async(_ task: @escaping Task, _ mainTask: @escaping Task) {
_async(task, mainTask)
}

public static func _async(_ task: @escaping Task, _ mainTask: Task? = nil) {
let item = DispatchWorkItem(block: task)
// 子线程执行任务
DispatchQueue.global().async(execute: item)
if let main = mainTask {
// 子线程执行完毕后通知主线程执行任务
item.notify(queue: DispatchQueue.main, execute: main)
}
}
}

二、延迟

2.1 普通延迟

在iOS中使用延迟一般使用dispatch_after,但是在Swift中没有这个API,使用的是DispatchQueue.*.asyncAfter

示例代码:

1
2
3
4
5
6
@discardableResult
public static func delay(_ seconds: Double, _ block: @escaping Task) -> DispatchWorkItem {
let item = DispatchWorkItem(block: block)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + seconds, execute: item)
return item
}

2.2. 异步延迟

示例代码(定义函数):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@discardableResult
public static func asyncDelay(_ seconds: Double, _ task: @escaping Task) -> DispatchWorkItem {
return _asyncDelay(seconds, task)
}

@discardableResult
public static func asyncDelay(_ seconds: Double, _ task: @escaping Task, _ mainTask: @escaping Task) -> DispatchWorkItem {
return _asyncDelay(seconds, task, mainTask)
}

private static func _asyncDelay(_ seconds: Double, _ task: @escaping Task, _ mainTask: Task? = nil) -> DispatchWorkItem {
let item = DispatchWorkItem(block: task)
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + seconds, execute: item)
if let main = mainTask {
item.notify(queue: DispatchQueue.main, execute: main)
}
return item
}

示例代码(使用):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ViewController: UIViewController {
private var item: DispatchWorkItem?

override func viewDidLoad() {
super.viewDidLoad()

item = Async.asyncDelay(3) {

};
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
item?.cancel()
}
}

为什么定义延迟异步时函数要返回DispatchWorkItem?是因为有可能需要对任务做取消或其他操作。

三、once

dispatch_once在Swift中已被废弃,取而代之的是静态属性(底层还是调用了dispatch_once)。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ViewController: UIViewController {
static var onceTask: Void = {
print("onceTask")
}()

override func viewDidLoad() {
super.viewDidLoad()

let _ = Self.onceTask
let _ = Self.onceTask
let _ = Self.onceTask
}
}
// 输出:print("onceTask")

四、加锁

当很多资源同时访问同一块数据的时候,可能会发生抢夺资源的情况,这时候需要一个加锁机制,当上次任务还未结束时,等待到任务完成才能继续下一次任务。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
public struct Cache {
private static var data = [String: Any]()
private static var lock = DispatchSemaphore(value: 1)
public static func set(_ key: String, _ value: Any) {
lock.wait()
defer {
lock.signal()
}
data[key] = value
}
}

lock.wait()是加锁,lock.signal()是取消锁(解锁)。加锁的API还有很多:NSLock、NSRecursiveLock等等,使用起来都很简单(只需要注意死锁问题即可)。