@ObservedObjectを知りたい我
@ObservedObjectを何となく使っていたので調べ直す。
とりあえず公式を見る。
概要
監視可能なオブジェクトをサブスクライブし、監視可能なオブジェクトが変更されるたびにビューを無効にするプロパティ ラッパー タイプ。
@MainActor @propertyWrapper @preconcurrency @frozen
struct ObservedObject<ObjectType> where ObjectType : ObservableObject
Add the @ObservedObject attribute to a parameter of a SwiftUI View when the input is an ObservableObject and you want the view to update when the object’s published properties change. You typically do this to pass a StateObject into a subview.
→ StateObjectをサブViewに渡すために、@ObservedObjectを使用すると記載がある。
次の例では、データ モデルを監視可能なオブジェクトとして定義し、ビューでモデルを状態オブジェクトとしてインスタンス化してから、そのインスタンスを監視対象オブジェクトとしてサブビューに渡します。
class DataModel: ObservableObject {
@Published var name = "Some Name"
@Published var isEnabled = false
}
struct MyView: View {
@StateObject private var model = DataModel()
var body: some View {
Text(model.name)
MySubView(model: model)
}
}
struct MySubView: View {
@ObservedObject var model: DataModel
var body: some View {
Toggle("Enabled", isOn: $model.isEnabled)
}
}
Don’t specify a default or initial value for the observed object. Use the attribute only for a property that acts as an input for a view, as in the above example.
→ @ObservedObjectにデフォルト値や初期値を指定しないでとの記載がある。@ObservedObject属性は親ViewやモデルからViewに提供されるデータを使用する。これにより、Viewがデータを所有するのではなく、単に観察することになる。初期値を指定することは、Viewがオブジェクトの状態を管理することを意味し、これは@ObservedObjectの目的に反する。
@StateObjectについても調べてみる。
概要
監視可能なオブジェクトをインスタンス化するプロパティ ラッパー タイプ。
@MainActor @frozen @propertyWrapper @preconcurrency
struct StateObject<ObjectType> where ObjectType : ObservableObject
ビュー階層に格納する参照型の唯一の真実のソースとして状態オブジェクトを使用します。プロパティ宣言に @StateObject 属性を適用し、ObservableObject プロトコルに準拠する初期値を提供することで、App、Scene、または View に状態オブジェクトを作成します。状態オブジェクトをプライベートとして宣言して、メンバー単位の初期化子から設定されないようにします。メンバー単位の初期化子は、SwiftUI が提供するストレージ管理と競合する可能性があります。
→ @StateObjectで宣言されたものが基準となる。
class DataModel: ObservableObject {
@Published var name = "Some Name"
@Published var isEnabled = false
}
struct MyView: View {
@StateObject private var model = DataModel() // Create the state object.
var body: some View {
Text(model.name) // Updates when the data model changes.
MySubView()
.environmentObject(model)
}
}
ObservedObject 属性を持つプロパティを介して、状態オブジェクトをサブビューに渡すことができます。または、上記のコードの MySubView のように、environmentObject(_:) 修飾子をビューに適用して、オブジェクトをビュー階層の環境に追加します。その後、EnvironmentObject 属性を使用して、MySubView またはその子孫内のオブジェクトを読み取ることができます。
→ EnvironmentObjectを使用して、サブView側で使用することができる。
struct MySubView: View {
@EnvironmentObject var model: DataModel
var body: some View {
Toggle("Enabled", isOn: $model.isEnabled)
}
}
以下の違いについて、ChatGPTに聞いてみた。
@StateObject private var model = DataModel()
private var model = DataModel()
・@StateObjectは、Viewが生成されタイミングで生成し、Viewが破棄されたタイミングで破棄
・@StateObjectは、再描画時にリセットされない
・@StateObjectは、監視を行い、変更があった場合に再描画処理を実行する
詳細
@StateObject
を使用するパターンと private var model = DataModel()
を使用するパターンの違いは、SwiftUIの状態管理の方法とライフサイクルにあります。以下にそれぞれの違いを説明します。
@StateObject
を使用するパターン
@StateObject private var model = DataModel()
-
ライフサイクル管理:
-
@StateObject
を使うと、SwiftUIはDataModel
のインスタンスをビューのライフサイクルに合わせて管理します。ビューが作成されたときにインスタンスが作成され、ビューが破棄されたときにインスタンスも破棄されます。 - ビューが再レンダリングされる際にも、同じインスタンスが使用されるため、状態が保持されます。
-
-
状態管理:
-
@StateObject
はそのインスタンスが変更されるたびにビューを再描画します。例えば、DataModel
のname
プロパティが変更されると、ビューの該当部分が自動的に更新されます。
-
-
使い方の場所:
- 主にビューのルートレベルや独立したビューに対して使用されます。
private var model = DataModel()
を使用するパターン
private var model = DataModel()
-
ライフサイクル管理:
- この場合、
DataModel
のインスタンスはビューの再レンダリング時に毎回新しいインスタンスが作成されます。ビューが再描画されるたびに新しいDataModel
が作成されるため、状態がリセットされます。
- この場合、
-
状態管理:
-
@Published
プロパティに対する変更はビューに通知されません。したがって、DataModel
のプロパティが変更されてもビューは再描画されません。
-
-
使い方の場所:
- 主にビューの一部で使い捨ての状態を保持する場合や、状態が他のビューと共有されない場合に使用されます。
結論
-
@StateObject
はビューのライフサイクルに合わせて状態を保持し、状態が変更されたときにビューを再描画するために使用します。 -
private var model = DataModel()
は一時的な状態を保持するために使用し、ビューが再描画されるたびに新しいインスタンスを作成します。
従って、MyView
では @StateObject
を使用することで、DataModel
の状態が正しく管理され、プロパティの変更がビューに反映されるようになります。
以下の違いについては、以下を見る。
@StateObject
@ObservedObject