Swift3でGrand Central Dispatchも大幅に変わっているので、簡単にまとめました。
dispatch_queue
concurrent queue(実行スレッドは複数で同時に複数タスク)やserial queue(実行スレッドは1つでタスクごとに違うスレッドで実行される可能性はあるが、同時に1タスク)の生成は、以下のように行います。
// concurrent queue
let concurrentQueue = DispatchQueue(label: "com.example.concurrent-queue", attributes: .concurrent)
// serial queue
let serialQueue = DispatchQueue(label: "com.example.serial-queue")
dispatch_get_global_queue
global queueはDispatchQoS.QoSClass
の優先度をもとに、下記のように取得することができます。
// 上から順に優先度が高いもの
enum QoSClass {
case userInteractive
case userInitiated
case default
case utility
case background
case unspecified
}
let globalQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
dispatch_get_main_queue
main queueは下記のように取得することができます。
let mainQueue = DispatchQueue.main
dispatch_async
非同期で処理を実行する場合は、下記のようになります。
let queue = DispatchQueue.global()
queue.async {
print("dispatch_async")
}
dispatch_sync
同期で処理を実行する場合は、下記のようになります。
let queue = DispatchQueue.global()
queue.sync {
print("dispatch_sync")
}
dispatch_after
Swift3ではインターバルを生成するために下記のenumが用意されています。
enum DispatchTimeInterval {
case seconds(Int)
case milliseconds(Int)
case microseconds(Int)
case nanoseconds(Int)
}
処理を指定した時間が経過してから実行する場合は、下記のようになります。
let dispatchTime: DispatchTime = DispatchTime.now() + DispatchTimeInterval.seconds(1)
DispatchQueue.global().asyncAfter(deadline: dispatchTime) {
print("dispatchTime = \(dispatchTime)")
}
dispatch_barrier_async
下記のコードのように0から99までループをさせて、10で割り切れる数字のみ文字列を置き換えて、それ以外の場合はprintするという処理を行います。
let queue = DispatchQueue(label: "com.sample.barrier", attributes: .concurrent)
var string = ""
for i in 0..<99 {
guard i % 10 == 0 else {
queue.async {
print("\(i) : string = " + string)
}
continue
}
queue.async {
let range = string.startIndex..<string.index(string.startIndex, offsetBy: string.characters.count)
string.removeSubrange(range)
string += "\(i)"
}
}
並列に非同期で処理しているので、removeSubrange完了時にprintが呼ばれている可能性があり、空文字列の状態がprintされてしまっているときがあります。
// 実行結果
4 : string =
7 : string = 0
3 : string =
1 : string =
6 : string =
2 : string =
5 : string = 0
8 : string = 0
9 : string = 0
下記のようにasync(flags: .barrier)
を使うことで、文字列操作が実行される際のタスクが1つになります。
let queue = DispatchQueue(label: "com.sample.barrier", attributes: .concurrent)
var string = ""
for i in 0..<99 {
guard i % 10 == 0 else {
queue.async {
print("\(i) : string = " + string)
}
continue
}
queue.async(flags: .barrier) {
let range = string.startIndex..<string.index(string.startIndex, offsetBy: string.characters.count)
string.removeSubrange(range)
string += "\(i)"
}
}
// 実行結果
6 : string = 0
7 : string = 0
8 : string = 0
9 : string = 0
11 : string = 10
13 : string = 10
12 : string = 10
15 : string = 10
14 : string = 10
dispatch_group
async
を使ったり、wait
を使う場合は、下記のようになります。
let group = DispatchGroup()
for i in 0..<100 {
DispatchQueue.global().async(group: group) {
print(i)
}
}
_ = group.wait(timeout: .distantFuture)
print("after wait")
// 実行結果
1
2
// 中略
98
99
after wait
enter
やleave
を使ったり、nofity
を使う場合は、下記のようになります。
let group = DispatchGroup()
for i in 0..<100 {
group.enter()
DispatchQueue.global().async {
print(i)
group.leave()
}
}
group.notify(queue: .global()) {
print("notify closure called")
}
print("after notify")
// 実行結果
1
2
// 中略
98
99
after notify
notify closure called
dispatch_once
Swift3ではdispatch_once
に相当するものがなくなりました。
シングルトンのインスタンスを一度だけ初期化するなどの処理は
class Hoge {
static let shared = Hoge()
}
のようにstatic
なPropertyをlet
で定義することで実現できます。
一方、一度だけ実行したい処理がある場合は
class ViewController: UIViewController {
lazy var __once: Void = {
self.printSomething()
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
_ = __once // Void型のPropertyの初期化が一度だけ実行されるので、`self.printSomething()`が呼ばれる
_ = __once // 既にPropertyの初期化が完了しているので、`self.printSomething()`が呼ばれない
}
func printSomething() {
print("this method might be executed once.")
}
}
のように、Void
型のPropertyをlazyで初期化を行い、その中で処理を実行することで実現できます。
上記の方法は
The free function
dispatch_once
is no longer available in Swift. In Swift, you can use lazily initialized globals or static properties and get the same thread-safety and called-once guarantees asdispatch_once
provided. Example:
let myGlobal = { … global contains initialization in a call to a closure … }()
_ = myGlobal // using myGlobal will invoke the initialization code only
のように[Migrating to Swift 2.3 or Swift 3 from Swift 2.2](https://swift.org/migration-guide/)に記載されています。