Observer — 変更のサブスクリプションパターンの主要概念

著者: IT Sectr 公開日: 2026-02-17 読了時間: 7 分

Observer — 1つのオブジェクト(発行者)が自身の状態変化について複数のサブスクライバーに通知する動作パターンです。モバイル開発において、Observerはリアクティブメカニズムの基盤です。UIがデータの変更を購読し、自動的に更新されます。このパターンはiOSのNotificationCenterとAndroidのLiveData/Flowに実装されています。詳細はRefactoring Guru: Observerをご覧ください。

重要なポイント

  • Observer — サブスクリプションパターン:1つの発行者、複数のサブスクライバー
  • NotificationCenter — iOS/macOSにおけるObserverの組み込み実装
  • FlowとLiveData — AndroidにおけるObserverのリアクティブ実装
  • Push vs Pull — 発行者はデータを送信するか、イベントを通知できます
  • メモリリーク — リークを防ぐため、サブスクライバーは購読を解除する必要があります

Observerとは:オブザーバーパターンの本質

Observer — オブジェクト間の一対多の依存関係を定義するGoFの動作パターンです。1つのオブジェクト(SubjectまたはObservable)が状態を変更すると、依存するすべてのオブジェクト(Observers)が自動的に通知され更新されます。このパターンは疎結合を実装します。発行者はサブスクライバーの具体的なクラスを知らず、Observerインターフェースを実装していることだけを知っています。

Observerの構造には、attach()、detach()、notify()メソッドを持つSubjectインターフェースと、update()メソッドを持つObserverインターフェースが含まれます。ConcreteSubjectは状態とサブスクライバーのリストを保存します。ConcreteObserverはupdate()を実装し、変更に反応します。モバイル開発では、GoFの古典的な実装は稀で、NotificationCenter、Combine、Flow、LiveDataといった組み込みメカニズムに置き換えられています。これらは同じアイデアを最新のAPIで実装しています。

Push vs Pullモデル — Pushモデルでは、Subjectがすべてのサブスクライバーにデータを送信します(NotificationCenter.post)。Pullモデルでは、Subjectは通知のみを行い、サブスクライバー自身がデータを要求します。AndroidのLiveDataはPushを使用し(データはobserve()で渡されます)、RxJava/Flowは両方のモデルをサポートしています。選択はタスクによります。PushはUI更新にシンプルで、Pullはサブスクライバーが受け取りたくない大量のデータに効率的です。

iOSのObserver:NotificationCenter、Combine、KVO

NotificationCenter — Observerを実装するためのiOS/macOSの組み込みメカニズムです。発行者はNotificationCenter.default.post(name:, object:, userInfo:)を介してNotificationを送信します。サブスクライバーはaddObserver(forName:, queue:, using:)を介して登録します。NotificationCenterは名前付き通知(Notification.Name)をサポートし、userInfoに任意のデータを渡せます。UIKeyboardWillShowNotification、UIApplicationDidEnterBackgroundNotification — システムの例です。

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

// 発行者
NotificationCenter.default.post(
    name: .userDidLogin,
    object: nil,
    userInfo: ["userId": "123"]
)

// サブスクライバー
class ProfileViewModel {
    private var observers: [NSObjectProtocol] = []

    func startObserving() {
        let observer = NotificationCenter.default.addObserver(
            forName: .userDidLogin,
            object: nil,
            queue: .main
        ) { [weak self] notification in
            guard let userId = notification.userInfo?["userId"] as? String else { return }
            // サブスクライバーがイベントに反応
            self?.loadProfile(userId: userId)
        }
        observers.append(observer)
    }

    func stopObserving() {
        observers.forEach { NotificationCenter.default.removeObserver($0) }
        observers.removeAll()
    }
}

Combineフレームワーク — iOS 13で導入されたNotificationCenterに代わるモダンなリアクティブ代替手段です。Publisher(NotificationCenter、URLSession、Timer)— 発行者、Subscriber(sink、assign)— 購読者。Combineはデータストリーム変換のための演算子(map、filter、combineLatest)を追加します。@Published — プロパティラッパーで、変更をサブスクライバーに自動的に通知します。SwiftUIを使用したMVVMでは、CombineがNotificationCenterに代わってViewModelとViewをバインドします。

KVO(Key-Value Observing) — オブジェクトの個別プロパティを監視するための従来のObjC/Swiftメカニズムです。@objc dynamic var name: String — 監視可能なプロパティ。observe(.name) — 購読。KVOは@objc互換のクラスとObjCの継承でのみ動作します。Appleは新しいプロジェクトではKVOの代わりにCombineと@Publishedを推奨しています。KVOはハイブリッドプロジェクトでのUIKit互換性のために引き続き重要です。

AndroidのObserver:LiveData、StateFlow、SharedFlow

LiveData — Observerを実装するためのAndroid Architecture Componentsのコンポーネントです。データ変更をサブスクライバーに通知する監視可能なクラスです。LiveDataはライフサイクルを認識します。サブスクライバー(LifecycleOwner)は破棄されると自動的に購読を解除します。LiveDataはPushモデルを使用します(データはobserve()で渡されます)。LiveDataはJetpack Compose以前のAndroidにおけるMVVMの基本的な構成要素です。

kotlin
// ViewModel — 発行者
class UserViewModel : ViewModel() {
    private val _user = MutableLiveData<User?>(null)
    val user: LiveData<User?> = _user

    fun loadUser(id: String) {
        viewModelScope.launch {
            val result = userRepository.getUser(id)
            _user.value = result
        }
    }
}

// Fragment — サブスクライバー
class UserFragment : Fragment() {
    private val viewModel: UserViewModel by viewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        viewModel.user.observe(viewLifecycleOwner) { user ->
            // サブスクライバーが変更に反応
            userName.text = user?.name
            userEmail.text = user?.email
        }
    }
}

StateFlowとSharedFlow — Jetpack ComposeでLiveDataに取って代わったKotlin Coroutinesのリアクティブ型です。StateFlow — 固定された現在値を持つ監視可能な状態ホルダー。SharedFlow — 状態を持たない設定可能なホットフローで、1回限りのイベント(ナビゲーション、トースト)に適しています。どちらの型もComposeと緊密に統合されています:collectAsState()、collectAsEffect()。Composeを使用するモダンなAndroidプロジェクトではStateFlowが必須です。

LiveData vs StateFlow — LiveDataはAndroidのライフサイクルに結びついていますが、StateFlowはプラットフォームに依存しません。StateFlowはコルーチン、演算子(map、filter)をサポートし、Androidの依存関係なしにテストできます。LiveDataはJavaとの互換性においてよりシンプルです。GoogleはKotlin + Composeを使用する新しいプロジェクトにはStateFlowを、レガシープロジェクトやJavaコードの維持にはLiveDataを推奨しています。

サブスクリプション管理とメモリリーク

メモリリーク — 適切なサブスクリプション管理なしのObserverの主要な問題です。サブスクライバー(Activity、Fragment、UIViewController)が破棄されたにもかかわらず購読が解除されていない場合、発行者はその参照を保持し続け、ガベージコレクターがメモリを解放できません。Androidでは、LifecycleOwner(Activity/Fragment)がremoveObserver()を呼び出すか、observe(viewLifecycleOwner)を使用する必要があります。iOSでは、deinit内でremoveObserver()を、またはCombineでdisposeBagを使用します。

プラットフォームObserverメカニズム自動購読解除手動購読解除
iOSNotificationCenterなしdeinit内のremoveObserver()
iOSCombine (sink)なしstore(in: &bag) — DisposeBag
iOSKVOなしdeinit内のremoveObserver()
AndroidLiveDataあり(LifecycleOwner)removeObserver()(オプション)
AndroidStateFlowviewModelScope経由解除時にcancel() Job
AndroidRxJavaなしCompositeDisposable内のdispose()

サブスクライバーにおける弱参照 — サブスクリプションブロックでクロージャーを使用する場合、Swiftでは[weak self]を、Kotlinではライフサイクルスコープを参照します。LiveDataはLifecycleOwnerを通じてサブスクリプションを自動的に管理します。サブスクリプションはLifecycleがSTARTEDまたはRESUMED状態のときにのみアクティブです。ComposeのStateFlowはライフサイクルを認識してcollectAsState()を使用します。iOSのNotificationCenterでは、クロージャーがselfへの強い参照を保持するため、明示的な[weak self]が必要です。

Observer vs 発行者-購読者:違い

Observer(GoF)と発行者-購読者(PubSub) — 類似していますが異なるパターンです。Observerでは、発行者がサブスクライバーのメソッドを直接呼び出して通知します。発行者はサブスクライバーを把握しています(リストを保持)。PubSubでは、発行者とサブスクライバーは互いを知らず、間に仲介者(Event Bus、Message Queue、NotificationCenter)が存在します。発行者はチャネルにメッセージを送信し、サブスクライバーはチャネルをリッスンします。PubSubはより疎結合です。

モバイル開発におけるPubSubの例 — iOSのNotificationCenterはPubSubと見なせます。発行者はサブスクライバーを知らず、単に通知を投稿します。AndroidのEventBusやOtto(非推奨)。BroadcastChannelを使用したSharedFlow — Kotlinの世界でのPubSub。分散システムでは、PubSubはRabbitMQ、Kafka、Google PubSubを通じて実装されます。モバイル開発において、PubSubはモジュール同士が依存すべきでないモジュラーアーキテクチャで有用です。

どちらを選ぶか — UI更新(ViewModel → View)には、Observer(LiveData、StateFlow、@Published)を使用します。モジュール間イベント(認証、ログアウト、テーマ変更)には、PubSub(SharedFlow、NotificationCenter、EventBus)を使用します。Observerは単一画面内ではよりシンプルで効率的ですが、PubSubはグローバルイベントに対してより柔軟ですが、暗黙的な依存関係のためデバッグが困難です。

よくある質問

StateFlowとLiveDataの違いは何ですか?

StateFlowはKotlin Coroutinesのプラットフォーム非依存型で、LiveDataはAndroidのライフサイクルに結びついています。StateFlowはコルーチンと演算子をサポートし、Androidなしでテストできます。LiveDataはLifecycleOwnerを通じて自動的にサブスクリプションを管理します。GoogleはKotlin + Composeを使用する新しいプロジェクトにはStateFlowを、Java互換性にはLiveDataを推奨しています。

NotificationCenterでメモリリークを回避するには?

ハンドラークロージャーで[weak self]を使用し、deinitでremoveObserver()を呼び出します。observer(NSObjectProtocol)への参照を保持し、オブジェクトが破棄されたら削除します。Combineでは、AnyCancellableとstore(in:)を使用して、DisposeBagが解放されたときに自動購読解除を行います。

SwiftUIでCombineなしでObserverを使用できますか?

はい、SwiftUIは@Publishedと@StateObject/@ObservedObjectを使用したObservableObjectをサポートしています。これはObserverの組み込み実装です。@Publishedは自動的にViewに変更を通知します。Combineは必須ではありません。ObservableObjectはSwiftUIに組み込まれたobjectWillChange Publisherを使用します。Combineはストリーム変換のための演算子を追加します。

StateFlowの代わりにSharedFlowを使用するのはいつですか?

SharedFlow — 現在値が不要な1回限りのイベント(ナビゲーション、トースト、Snackbar)に使用します。StateFlow — 現在のスナップショットが必要なUI状態(データリスト、読み込み進捗)に使用します。SharedFlowにはvalueプロパティがなく、新しいサブスクライバーに最後の値を返しません。

iOSにおけるKVOとCombineの違いは何ですか?

KVOは従来のObjCメカニズムで、@objc dynamicが必要であり、NSObjectから継承されたクラスでのみ動作します。CombineはモダンなSwiftフレームワークで、型安全であり、演算子とSwiftUI統合を備えています。CombineはKVOとNotificationCenterを置き換えます。Appleは新しいプロジェクトにはCombineを、レガシーサポートにはKVOのみを推奨しています。

まとめ

  • Observer — サブスクライバーに変更を通知する動作パターン
  • iOS NotificationCenter — 名前付き通知を使用したPubSub実装
  • iOS Combine — PublisherとSubscriberを備えたリアクティブフレームワーク
  • Android LiveData — Android Architecture Componentsのライフサイクル認識Observer
  • Android StateFlow — Composeとコルーチンのためのリアクティブ状態ホルダー
  • メモリ管理 — リーク防止のための必須の購読解除
  • Observer vs PubSub — 疎結合のための直接購読 vs 仲介者

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください