@StateObject: その概要、ObservableObjectの作成と管理

著者: IT Sectr 公開日: 2026-06-26 読了時間: 9 分

@StateObjectはSwiftUIのproperty wrapperであり、Viewのライフサイクル全体にわたってObservableObjectインスタンスを作成し、所有します。Viewが初めて画面に表示されるとき、@StateObjectはオブジェクトを初期化し、Viewがメモリから削除されるまでそれを保持します。これにより、インターフェースの再構築時(テーマの変更や親Viewの更新など)にデータがリセットされないことが保証されます。Apple Developer Documentation(2025)によると、@StateObjectはSwiftUI階層内でObservableObjectの主要な情報源(source of truth)として使用されるべきであり、子Viewは@ObservedObjectまたは@EnvironmentObjectを介して既に作成されたオブジェクトを受け取ります。

主なポイント

  • @StateObject — View内でObservableObjectを作成し所有するためのproperty wrapper。
  • 一度の作成 — オブジェクトはViewのライフタイム中に一度だけ初期化され、再構築時に再作成されません。
  • 情報源 — @ObservedObjectとは異なり、@StateObjectは階層内の情報源(source of truth)です。
  • ライフサイクル — オブジェクトはViewがメモリに存在する限り生き続け、Viewと共に破棄されます。
  • 初期化 — @StateObjectは作成時に初期値を必要とし、通常はパラメータ付きのinitを介して行います。

SwiftUIにおける@StateObjectとは

@StateObjectはiOS 14で導入されたproperty wrapperで、ViewがObservableObjectプロトコルに準拠するクラスのインスタンスを作成し、所有することを可能にします。値型(struct)で動作する@Stateとは異なり、@StateObjectは参照型 — プロパティの変更をSwiftUIに通知できるクラス — 向けに設計されています。

Viewが@StateObject var viewModel: MyViewModelを使用すると、SwiftUIはViewが最初に表示されるときにMyViewModelのインスタンスを自動的に作成し、フレームワークの特別なストレージに保存します。Viewが更新されるたび(たとえば、親の状態が変わったとき)、SwiftUIはオブジェクトを再作成せず、Viewが階層から削除されるまで既存のインスタンスを使用します。

Apple WWDC Session 10137(2024)によると、@StateObjectはiOS 13でViewが再構築されたときに存在していたデータ損失問題を解決します。それまでは開発者は親ViewでObservableObjectを作成し、イニシャライザを介して渡す必要があり、コードの重複や誤ったオブジェクト再作成のリスクがありました。

swift
import SwiftUI

class CounterViewModel: ObservableObject {
    @Published var count: Int = 0
    
    func increment() {
        count += 1
    }
}

struct CounterView: View {
    @StateObject var viewModel = CounterViewModel()
    
    var body: some View {
        VStack {
            Text("Count: \(viewModel.count)")
            Button("Increment", action: viewModel.increment)
        }
    }
}

@StateObjectの仕組み

@StateObjectのメカニズムは、SwiftUIとCombineフレームワークの統合に基づいています。ObservableObjectがそのプロパティを@Published属性でマークすると、SwiftUIはObservableObjectプロトコルに組み込まれたパブリッシャーを介して変更を自動的に購読します。公開されたプロパティが変更されると、オブジェクトはobjectWillChangeパブリッシャーを通じてシグナルを送信し、このオブジェクトを監視するすべてのViewの再描画をトリガーします。

SwiftUIはObservableObjectインスタンスを特定のViewインスタンスに紐付けられた特別なストレージに保存します。このストレージは最初のレンダリング時に一度作成され、Viewが破棄されるまで存在します。これが@StateObjectが参照の安定性を保証する理由です — SwiftUIはViewのイニシャライザに依存せず、メモリを自動的に管理します。

objc.io — Thinking in SwiftUI(2025)によると、@StateObjectの内部実装は@Stateと同様のメカニズムを使用しますが、参照型用です。SwiftUIはオブジェクトの周りにボクシングラッパーを作成し、頻繁なView階層の再構築に最適化された独自のアロケーターを介してライフサイクルを管理します。

@StateObjectのライフサイクル

  • 作成 — Viewが最初に画面に表示されるとき、SwiftUIはオブジェクトのイニシャライザを呼び出し、参照を保存します。
  • 再構築 — 親Viewが更新されるとき、オブジェクトは再作成されず、既存のインスタンスが使用されます。
  • 破棄 — Viewが画面から去り階層から削除されるとき、SwiftUIはオブジェクトのdeinitを呼び出します。

@StateObject vs @ObservedObject: 主な違い

@StateObject@ObservedObjectの主な違いは、誰がオブジェクトを所有するかです。@StateObjectはオブジェクトを作成し保存します — それが所有者です。@ObservedObjectは、他の場所で作成されイニシャライザやプロパティを介して渡されたオブジェクトを監視するだけです。

特性@StateObject@ObservedObject
所有権オブジェクトを作成し所有する監視のみ
初期化View内でinit/デフォルトを介して外部、パラメータを介して渡される
ライフサイクルViewのライフサイクルに紐付くViewによって制御されない
再作成更新時に再作成されない外部から置き換えられる可能性がある
iOSバージョンiOS 14+iOS 13+

ルールはシンプルです。ViewがObservableObjectを作成する場合は@StateObjectを使用します。Viewが親から既に作成されたオブジェクトを受け取るだけの場合は@ObservedObjectを使用します。このルールに違反すると、データ損失(所有権に@ObservedObjectを使用した場合)または過剰なオブジェクト作成(監視に@StateObjectを使用した場合)のいずれかが発生します。

@StateObjectを使用するタイミング

@StateObjectは、特定のデータセットの情報源となるViewで使用する必要があります。典型的なシナリオには、独自のview modelを持つ画面、ナビゲーションスタックのルート画面、および自身の状態を管理するモーダル表示が含まれます。

  • view modelを持つ画面 — 自身のデータとロジックを管理する各画面は、@StateObjectを介してview modelを作成する必要があります。
  • ルートView — NavigationStackやTabViewの階層では、ルート要素がデータを作成し、子要素は@ObservedObjectを介してデータを受け取ります。
  • モーダルウィンドウ — .sheetや.fullScreenCoverは、フォームやプロセスを管理するために独自の@StateObjectを必要とすることがよくあります。
  • 編集可能なリスト — 編集フォームを含むリストの各行は、独自の@StateObjectを持つ必要があります。
swift
struct ProfileView: View {
    @StateObject var viewModel = ProfileViewModel()
    
    var body: some View {
        NavigationStack {
            Form {
                TextField("Name", text: $viewModel.name)
                TextField("Email", text: $viewModel.email)
                Button("Save") {
                    viewModel.saveProfile()
                }
            }
            .navigationTitle("Profile")
        }
    }
}

パラメータ付きで@StateObjectを初期化する

@StateObjectをパラメータ付きで初期化するには特別な構文が必要です。なぜならSwiftUIがオブジェクト作成を自身で管理するからです。イニシャライザに単にパラメータを渡すことはできません — エスケープクロージャまたは別のファクトリメソッドを使用する必要があります。

Swift by Sundell(2024)によると、最もクリーンな方法は、SwiftUIがオブジェクトを最初に作成するときに呼び出すファクトリメソッドまたはクロージャを使用することです。別のアプローチとしては、親ViewでObservableObjectを初期化し、標準のイニシャライザを使用して@StateObjectを介して渡す方法があります。

swift
class UserViewModel: ObservableObject {
    @Published var user: User
    
    init(user: User) {
        self.user = user
    }
}

struct UserDetailView: View {
    @StateObject var viewModel: UserViewModel
    
    init(user: User) {
        _viewModel = StateObject(wrappedValue: UserViewModel(user: user))
    }
    
    var body: some View {
        Text(viewModel.user.name)
    }
}

@StateObjectを使用するViewのイニシャライザは、property wrapper自体にアクセスするためにプロパティ名の前にアンダースコア(_viewModel)を使用する必要があることに注意してください。これはイニシャライザでproperty wrapperを扱うための標準的なSwiftパターンです。

@StateObjectのよくある間違い

最も一般的な間違いは、オブジェクトを所有すべきViewで@StateObjectの代わりに@ObservedObjectを使用することです。この場合、親が再構築されるたびにオブジェクトが再作成され、蓄積されたすべてのデータが失われます。この間違いは、NavigationStackやTabViewを使用する複雑な階層で特に厄介です。

  • ナビゲーション中のデータ損失 — 子画面が自身のview modelに@ObservedObjectを使用している場合、戻って再度開くときにデータがリセットされます。
  • メモリリーク — 決して削除されない親Viewで@StateObjectを作成すると、各子画面も制御なしに@StateObjectを作成する場合、オブジェクトが蓄積される可能性があります。
  • オブジェクトの重複 — 単一のObservableObjectを異なるViewの複数の@StateObjectに渡すと、互いに同期しない独立したインスタンスが複数作成されます。

これらの問題を避けるために、シンプルなルールに従ってください。情報源ごとに1つの@StateObject。データを複数の画面で共有する必要がある場合は、ルートViewで@StateObjectを一度作成し、@ObservedObjectまたは@EnvironmentObjectを介して子要素に渡します。

swift
// ❌ Wrong: @ObservedObject for owning an object
struct BadView: View {
    @ObservedObject var vm = ViewModel() // will be recreated on each update!
}

// ✅ Correct: @StateObject for owning
struct GoodView: View {
    @StateObject var vm = ViewModel() // created once for View lifetime
}

よくある質問

@StateObjectと@Stateの違いは何ですか?

@Stateは値型(struct、文字列、数値)で動作し、値を直接SwiftUIストレージに保存します。@StateObjectは参照型 — ObservableObjectに準拠するクラス — で動作します。@Stateは単純なローカル状態に適しており、@StateObjectはロジックと公開プロパティを持つ複雑なオブジェクトに適しています。

iOS 13で@StateObjectを使用できますか?

いいえ、@StateObjectはiOS 14以降でのみ利用可能です。iOS 13の場合は、@ObservedObjectを使用し、手動のライフサイクル管理で@Stateを介して親ViewにObservableObjectを作成してください。代替案として、参照セマンティクスを必要としないデータには、classの代わりにstructで@Stateを使用する方法があります。

親から渡されたオブジェクトを子Viewで@StateObjectを使用するとどうなりますか?

子ViewはObservableObjectの独自のコピーを作成し、親とは完全に独立したものになります。一方の変更は他方に影響しません。これはほとんどの場合エラーです。親からオブジェクトを受け取るには@ObservedObjectを使用し、View内で新しいオブジェクトを作成する場合にのみ@StateObjectを使用してください。

@StateObjectで作成されたオブジェクトはいつ破棄されますか?

オブジェクトは、それを作成したViewがSwiftUI階層から完全に削除されたときに破棄されます。NavigationStackの画面の場合、ナビゲーションスタックからポップされたときに発生します。モーダルウィンドウの場合は、閉じられたときです。TabViewの場合は、Viewがキャッシュされていない場合、タブを切り替えたときです。

初期化時に@StateObjectにパラメータを渡すにはどうすればよいですか?

アンダースコアを介してproperty wrapperにアクセスするカスタムinitを使用します。_viewModel = StateObject(wrappedValue: MyViewModel(param: value))。このパターンにより、Viewのライフタイム中の単一オブジェクト作成の保証を維持しながら、任意のパラメータをObservableObjectに渡すことができます。

まとめ

  • @StateObject — View内でObservableObjectを作成し所有するためのproperty wrapper。iOS 14以降で利用可能。
  • 単一作成の保証 — オブジェクトは一度初期化され、Viewの再構築時に再作成されません。
  • 情報源 — @StateObjectは情報源であり、@ObservedObjectは単なるオブザーバーです。
  • ライフサイクル — オブジェクトはViewがSwiftUI階層に存在する限り生き続け、離れるときに破棄されます。
  • パラメータ付き初期化 — _viewModelとStateObject(wrappedValue:)を介したproperty wrapperへのアクセスが必要です。
  • 所有権の誤り — オブジェクト作成に@ObservedObjectを使用すると、再構築時にデータ損失が発生します。
  • 1つのオブジェクト — 1つの@StateObject — 共有データの場合、ルートViewで@StateObjectを作成し、@ObservedObjectを介して子に渡します。

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

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

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

こちらもお読みください