ObservableObjectクラス、staticクラスの生成順
SwiftにおけるObservableObjectクラスのstaticプロパティ(静的プロパティ)の生成順序は、通常のSwiftクラスと同じルールに従います。SwiftUI特有の動作ではなく、Swift言語の仕様に基づいています。
具体的な生成順序は以下の通りです。
1. 静的プロパティの初期化
クラスの静的プロパティは、そのクラスが初めて使用される直前に初期化されます。ここでいう「使用」とは、インスタンスの作成、静的メソッドの呼び出し、または静的プロパティへのアクセスなどを指します。
定義順に、ファイルの上から下へと順次初期化されます。
2. インスタンスの初期化
静的プロパティの初期化が完了した後、そのクラスのインスタンスが生成される際に、インスタンスプロパティの初期化とinit()(イニシャライザ)の実行が行われます。
ObservableObjectにおける注意点
SwiftUIでは、@ObservedObjectや@StateObjectなどのプロパティラッパーを通じてObservableObjectのインスタンスがビューのライフサイクルと連携して生成されます。
- 静的プロパティの初期化は、ビューの初期化や
ObservableObjectのインスタンス生成より前に行われることが多いです。これは、ビューが表示される前にアプリの初期設定などでクラスが参照される可能性があるためです。 - ただし、静的プロパティの初期化順序は保証されますが、どのタイミングで静的初期化が実行されるかは、実行時のクラスの使用状況に依存します。
例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class MyObservableObject: ObservableObject { static let staticProperty1: String = { print("1. Static property 1 initialized") return "Value 1" }() static let staticProperty2: String = { print("2. Static property 2 initialized") return "Value 2" }() @Published var instanceProperty = "" init() { print("3. Instance initialized") self.instanceProperty = MyObservableObject.staticProperty1 } } |
このコードを実行すると、MyObservableObjectが初めて使われた時点で、静的プロパティ1、静的プロパティ2の順に初期化され、その後インスタンスが生成される際にinit()が呼ばれます。
具体例1
|
1 2 |
class ResourceManager { static let shared = ResourceManager() } class test: ObservableObject{} |
ResourceManager.sharedとtestクラス、それぞれがどのように初期化されるかについてですね。
結論から言うと、この2つの定義だけでは、どちらが先に初期化されるかは決まっていません。
初期化の順序は、それぞれのクラスが実際にコードの中で最初に使われるタイミングに依存します。
解説
Swiftのクラスの初期化は「遅延実行 (lazy initialization)」されるためです。ResourceManager.shared の場合static let shared = ResourceManager() は、シングルトンパターンの定義です。
このsharedインスタンスは、コード内のどこかで ResourceManager.shared と最初に呼び出されたときに初めて生成されます。それまでは生成されません。
test: ObservableObject の場合
testクラス自体は、クラス定義が読み込まれただけではインスタンス化されません。
このクラスのインスタンスは、let t = test() のようにインスタンスが作成されたり、SwiftUIのビューで @StateObject var testObject = test() のように宣言されたりして、実際にインスタンスが必要になったタイミングで生成されます。
どちらが先か?
例えば、アプリケーションの起動直後に以下のようなコードがあった場合を考えてみましょう。
|
1 2 3 4 5 6 7 8 9 |
// AppDelegate または アプリのエントリポイント // (A) ResourceManager.sharedが先に呼ばれる let manager = ResourceManager.shared print("Manager initialized") // (B) testのインスタンスが後で作成される let testInstance = test() print("Test initialized") |
この場合、ResourceManager.sharedが先に初期化されます。
逆に、ResourceManager.sharedが全く使われず、testクラスのインスタンスだけが使われるようなビューが最初に表示された場合は、testクラスのインスタンスが先に初期化されます。
まとめ
ResourceManager.sharedは、ResourceManager.sharedにアクセスした初回に初期化されます。testインスタンスは、test()コンストラクタを呼び出した初回に初期化されます。
どちらのコード行がアプリケーションの実行フローで先に到達するかによって、初期化順序が決まります。
具体例2
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class obj: ObservableObject{ @Published var a: String = "lol" init (){ print("obj") } } class obj2 { static var aaa = obj2() init (){ print("obj2") } func test(){ print("obj2test") } } struct TestTest: View { @EnvironmentObject var www: obj var body: some View { VStack{ Text("\(www.a)") } .onAppear { obj2.aaa.test() } } } |
結果
obj
obj2
obj2test
onAppear がなければ obj2 obj2test は呼ばれない

コメント