Main Thread で動作させたいコードなどが時々あります。 UITableView
の reloadData()
などは、場合によって、autolayout を background thread でやるなみたいなメッセージをもらったり、Core Data や OpenGL のように やっぱりちゃんと main thread で呼び出したい場合などが考えられます。
もっともオーソドックスなやり方は、以下のような書き方かもしれません。
dispatch_async(dispatch_get_main_queue()) { () -> Void in
self.tableView.reloadData()
}
さてしかし、処理の順番などなんらかの都合で、dispatch_sync()
を使いたい時は、こんなコードを書きたいと一瞬思うかもしれませんが、これを、main thread から呼び出してはいけません。
doStep1() // 時間のかかる処理等
dispatch_sync(dispatch_get_main_queue()) { () -> Void in
doStep2() // step1 の結果を保存・表示する場合に main thread が必要な場合
}
doStep3() // 後始末等
ならば、NSThread.isMainThread()
で main thread かどうか、判定すればいいと思うかもしれませんが、同じコードを二回書かなくてはなりません。
if NSThread.isMainThread() {
// your code
}
else {
dispatch_sync(dispatch_get_main_queue()) { () -> Void in
// your code
}
}
で、技巧的に走って
let yourCode: () -> Void = {
// your code
}
if NSThread.isMainThread() {
yourCode()
}
else {
dispatch_sync(dispatch_get_main_queue()) { () -> Void in
yourCode()
}
}
と書くこともできますが、似たような処理の都度何度もこんな書き方をするのは面倒で、なおかつ可読性が落ちます。そこで、より使いかってを考えれば、そもそも引数としてコードを渡せるので、同期用と非同期用の二種類の関数を用意すれば便利になりそうです。
dispatch_sync_main.swift
func dispatch_sync_main(block: () -> Void) {
if NSThread.isMainThread() {
block()
}
else {
dispatch_sync(dispatch_get_main_queue()) { () -> Void in
block()
}
}
}
dispatch_async_main.swift
func dispatch_async_main(block: () -> Void) {
dispatch_async(dispatch_get_main_queue()) { () -> Void in
block()
}
}
こんな感じで、使いかってがよくなると思います。
doStep1()
dispatch_sync_main() {
doStep2()
}
doStep3()
dispatch_async_main() {
self.tableView.reloadData()
}
ネーミングが本物のGCDとまぎわらしいと思う場合は、_
をつけるなり、他の名前をつけるなり工夫しましょう。