ottijp blog

NotificationCenterのaddObserverやCombineのsinkは同期実行である

  • 2024-03-16

環境

  • Xcode: 15.1

調べたこと

NotificationCenteraddObserverにおける処理や,Combine(Publisher)のsinkにおける処理は非同期だと勝手に思っていたのですが(恥), デバッグ実行していて「もしや同期実行なのでは・・・?」と不安になったのでPlaygroundで試してみました.(その通り同期実行でした.)

また,オブザーバ(サブスクライバ)の実行スレッドがどうなるのかも併せて確認しました.

NotificationCenter

以下のようなコードで試してみました.

import Foundation

func log(_ line: String) {
  print("\(Date()) \(line)")
}

extension Notification.Name {
  static let sampleNotification = Notification.Name("sampleNotification")
}

NotificationCenter.default.addObserver(forName: .sampleNotification, object: nil, queue: .none) { notification in
  Thread.sleep(forTimeInterval: 2.0)
  log("observer (queue=none): \(Thread.current.description)")
}

NotificationCenter.default.addObserver(forName: .sampleNotification, object: nil, queue: .main) { notification in
  Thread.sleep(forTimeInterval: 2.0)
  log("observer (queue=main): \(Thread.current.description)")
}

Task {
  log("before post notification: \(Thread.current.description)")
  NotificationCenter.default.post(name: .sampleNotification, object: nil)
  log("after post notification: \(Thread.current.description)")
}
2024-03-16 10:22:53 +0000 before post notification: <NSThread: 0x60000171c140>{number = 4, name = (null)}
2024-03-16 10:22:55 +0000 observer (queue=none): <NSThread: 0x60000171c140>{number = 4, name = (null)}
2024-03-16 10:22:57 +0000 observer (queue=main): <_NSMainThread: 0x60000170c000>{number = 1, name = main}
2024-03-16 10:22:57 +0000 after post notification: <NSThread: 0x60000171c140>{number = 4, name = (null)}

はい,完全に同期実行でした. スレッドは.none(もしくはnil)を指定した場合はpostしたスレッドと同じスレッドで,.mainを指定した場合はメインスレッドで実行されますが,いずれも同期実行です.

Combine

以下のようなコードで試してみました.

import Foundation
import Combine

func log(_ line: String) {
  print("\(Date()) \(line)")
}

let subject = PassthroughSubject<Int, Never>()

subject.sink { value in
  Thread.sleep(forTimeInterval: 2.0)
  log("received: \(value) \(Thread.current.description)")
}

Task {
  log("before send: \(Thread.current.description)")
  subject.send(42)
  log("after send: \(Thread.current.description)")
}
2024-03-16 10:33:34 +0000 before send: <NSThread: 0x60000170c340>{number = 6, name = (null)}
2024-03-16 10:33:36 +0000 received: 42 <NSThread: 0x60000170c340>{number = 6, name = (null)}
2024-03-16 10:33:36 +0000 after send: <NSThread: 0x60000170c340>{number = 6, name = (null)}

はい,こちらも完全に同期実行でした. スレッドはsendしたスレッドと同じスレッドでした.

一般的にPub/Subという名前からは非同期実行をイメージしてしまうかもしれないので,要注意ですね.


ottijp
都内でアプリケーションエンジニアをしています
© 2024, ottijp