[Swift]ヘッドフォンを抜き差しするとAVAudioEngineがクラッシュするときの対処方の続き。
「NSNotificationCenterを使ってヘッドフォン抜き差し時のトリガーができたぜ、やったぜ」と思ってたのに、ボタンのタイトルとかラベルのテキストがうまく変わらないじゃないか!
いろいろ調べた結果、dispatch_async(dispatch_get_main_queue())を使ったらなんとかなった。
ダメだったコード
func configChange(notification:NSNotification) {
self.button.setTitle("PLAY", forState: .Normal)
self.labelRate.text = "100%"
}
これだと、ヘッドフォンを抜き差しして10秒くらい待ったら、ボタンタイトル・ラベルテキストが変わるときもある、変わらない時もある。なぜだ!
解決策
func configChange(notification:NSNotification) {
dispatch_async(dispatch_get_main_queue(), {
self.button.setTitle("PLAY", forState: .Normal)
self.label.text = "100%"
})
}
こうしたらできた。
原因
StackOverflowでSwift alternative to performSelectorOnMainThreadとかProblems with NSNotificationCenter and UIPickerViewとか参考にしたところ、
Notificationが送られるThreadが違う
ということらしい。
The issue is likely that the notification is sent (and therefore received) on a different thread than the main thread. Only on the main thread will you be able to update UI elements (like a label).
なるほど、ラベルとかのUI elementの情報をupdateするにはmain thread上でなければならないが、notificationが(私の場合はfunc configChange)、main threadではなく、他のthreadに送られているのだ、ということらしい。
だいたい今のいままで、threadとかいうのが複数あるのも知らんかった。。。
threadの確認
じゃあ、ヘッドフォン抜き差しするときのfunc congigChangeは、どのthreadで行なわれているのよ?と確認してみる。
func configChange(notification:NSNotification) {
println("config change %@", NSThread.currentThread())
}
上記コード書いて、Xcodeに実機繋げてヘッドフォン抜き差ししてみると、thread3とかthread4とかthread5とか、いろんなthreadで動作しているのがわかる。やっぱこれが原因なのね。
main threadで動かすには
dispatch_async(dispatch_get_main_queue(), {})というのを使えばいいらしい。
func configChange(notification:NSNotification) {
dispatch_async(dispatch_get_main_queue(), {
println("config change %@", NSThread.currentThread())
})
}
これで確認してみると、たしかに、ヘッドフォン抜き差しのたびにmain threadで動いていることがわかる。なるほどねー。
なお、かつてはperformSelectorOnMainThreadなるものがあったそうだが、古いから使っちゃダメよとのこと。
ちょっと賢くなった気がする。
以上!