スレッディング - Swift SDK
項目一覧
iOS および vOS アプリを高速で応答性を高くするには、ビジュアルを配置し、ユーザー インタラクションを処理するのに必要な時間と、データを処理し、ビジネス ロジックの実行に必要な時間とのバランスを取る必要があります。 通常、アプリ開発者はこの作業を複数のスレッドに分散します。そのため、ユーザー インターフェイスに関連するすべての作業のメイン スレッドまたは UI スレッドと、提示のために UI スレッドに送信する前に、より重いワークロードを計算するための 1 つ以上のバックグラウンド スレッドがあります。 重い作業をバックグラウンド スレッドにオフロードすることで、ワークロードのサイズに関係なく、UI スレッドの応答性が非常に高くなります。 しかし、デッドロックや競合状態などの問題を回避する、スレッドセーフでパフォーマンスが高く、維持可能なマルチスレッド コードを記述するのは非常に困難です。 Realm はこれを簡素化することを目的としています。
Tip
以下も参照してください。
10.26.0現在、 Realm は、バックグラウンド書込みを実行するための非同期書込みメソッドを提供します。 詳細: バックグラウンド書込みを実行するを参照してください。 非同期書込みでは、スレッドセーフな参照や固定されたオブジェクトをスレッド間で渡す必要はありません。
このページでは、スレッド全体で邦土ファイルとオブジェクトを手動で管理する方法について説明します。 Realmは Swiftアクター の使用もサポートしています。 Swift同時実行機能を使用して邦土アクセスを管理します。 Realm のアクター サポートの概要については、「 アクターで Realm を Realmする - Swift SDK」を参照してください。
従う 3 つのルール
Realm のマルチスレッド アプリ用ツールを調べる前に、次の 3 つのルールを理解し、従う必要があります。
- 読み取りをロックしないでください。
- Realm のMultiversion Concurrency Control (MVCC)アーキテクチャにより、読み取り操作のためにロックする必要がなくなります。 読み込んだ値が破損したり、部分的に変更された状態になったりすることはありません。 ロックやミューテックスを必要とせずに、任意のスレッドで同じ Realm ファイルから自由に読み取りができます。 各スレッドは読み取りを行う前にすべてのスレッドを待機する必要があるため、不必要にロックするとパフォーマンスのボトルネックが発生します。
- バックグラウンド スレッドで書き込みを行う場合は、UI スレッドでの同期書き込みを避けます。
- Realm ファイルにはどのスレッドからでも書き込みができますが、一度に書き込みできるのは 1 つだけです。 その結果、同期書込みトランザクション (write transaction) は相互にブロックされます。 UI スレッドで同期書込み (write) を行うと、バックグラウンド スレッドへの書込みが完了するのを待機している間にアプリが応答しなくなる可能性があります。 Device Syncはバックグラウンド スレッドで書込み (write) を実行するため、同期された Realm を持つ UI スレッドでの同期書込みを避ける必要があります。
- ライブ オブジェクト、コレクション、または Realm を他のスレッドに渡さないでください。
- ライブ オブジェクト、コレクション、Realm インスタンスはスレッド定義です。つまり、これらは作成されたスレッドでのみ有効です。 具体的には、ライブ インスタンスを他のスレッドに渡すことはできません。 ただし、Realm にはスレッド間でオブジェクトを共有するためのいくつかのメカニズムが用意されています。
バックグラウンド書き込みの実行
バージョン 10.26.0 の新機能。
writeAsync を使用して、バックグラウンドでオブジェクトを追加、変更、または削除できます。
writeAsync
を使用すると、スレッドセーフな参照や固定されたオブジェクトをスレッド間で渡す必要がありません。 代わりに、 realm.writeAsync
を呼び出します。 書き込みが完了または失敗した後にソース スレッドで実行されるメソッドの完了ブロックを指定できます。
バックグラウンド書込みを実行する際に考慮する必要があること
非同期書込み (write) は、Realm を閉じるまたは無効にすることをブロックします
トランザクションを明示的にコミットまたはキャンセルできます
let realm = try! Realm() // Query for a specific person object on the main thread let people = realm.objects(Person.self) let thisPerson = people.where { $0.name == "Dachary" }.first // Perform an async write to add dogs to that person's dog list. // No need to pass a thread-safe reference or frozen object. realm.writeAsync { thisPerson?.dogs.append(objectsIn: [ Dog(value: ["name": "Ben", "age": 13]), Dog(value: ["name": "Lita", "age": 9]), Dog(value: ["name": "Maui", "age": 1]) ]) } onComplete: { _ in // Confirm the three dogs were successfully added to the person's dogs list XCTAssertEqual(thisPerson!.dogs.count, 3) // Query for one of the dogs we added and see that it is present let dogs = realm.objects(Dog.self) let benDogs = dogs.where { $0.name == "Ben" } XCTAssertEqual(benDogs.count, 1) }
非同期書込みの完了を待機する
SDK は、Realm が現在非同期書込みを実行しているかどうかを示すBool
を提供します。 isPerformingAsynchronousWriteOperations変数は、次のいずれかを呼び出した後にtrue
になります。
writeAsync
beginAsyncWrite
commitAsyncWrite
スケジュールされたすべての非同期書込み操作が完了するまで true のままになります。 これは true ですが、Realm を閉じるか無効にすることをブロックします。
非同期書込みのコミットまたはキャンセル
非同期書込みを完了するには、ユーザーまたは SDK は次のいずれかを呼び出す必要があります。
writeAsync
メソッドを使用すると、SDK はトランザクションのコミットまたはキャンセルを処理します。 This provides the convenience of the async write without the need to manually keep state tied to the scope of the object. ただし、 writeAsync
ブロック内では、 commitAsyncWrite
またはcancelAsyncWrite
を明示的に呼び出すことができます。 これらのメソッドのいずれかを呼び出せずに戻る場合、 writeAsync
は次のいずれかになります。
書込みブロック内の指示を実行した後に書込みをコミットする
エラーを返します
どちらの場合も、これによりwriteAsync
操作が完了します。
非同期書込みトランザクションをコミットまたはキャンセルするタイミングをより詳細に制御するには、 beginAsyncWrite
メソッドを使用します。 このメソッドを使用する場合は、トランザクションを明示的にコミットする必要があります。 非同期書込み (write) をコミットせずに戻ると、トランザクションがキャンセルされます。 beginAsyncWrite
はcancelAsyncWrite
に渡すことができる ID を返します。
commitAsyncWrite
は書込みトランザクションを非同期にコミットします。 これは、データを Realm に永続化するステップです。 commitAsyncWrite
にはonComplete
ブロックが含まれます。 。 このブロックは、コミットが完了するかエラーで失敗すると、ソース スレッドで を実行します。
commitAsyncWrite
を呼び出すと、すぐに が返されます。 これにより、SDK がバックグラウンド スレッドで I/O を実行している間にも、呼び出し元を続行できます。 このメソッドはcancelAsyncWrite
に渡すことができる ID を返します。 これにより、完了ブロックの保留中の呼び出しがキャンセルされます。 コミット自体はキャンセルされません。
commitAsyncWrite
への連続した呼び出しをグループ化できます。 これらの呼び出しをバッチすることで、書込みパフォーマンスが向上します。特にバッチされたトランザクションが小さい場合。 グループ化トランザクションを許可するには、 isGroupingAllowed
パラメータをtrue
に設定します。
beginAsyncWrite
またはcommitAsyncWrite
でcancelAsyncWrite
を呼び出すことができます。 これをbeginAsyncWrite
で呼び出すと、書込みトランザクション全体がキャンセルされます。 commitAsyncWrite
でこれを呼び出すと、 commitAsyncWrite
に渡した可能性のあるonComplete
ブロックのみがキャンセルされます。 コミット自体はキャンセルされません。 キャンセルするbeginAsyncWrite
またはcommitAsyncWrite
の ID が必要です。
スレッド間の通信
異なるスレッドから同じ Realm ファイルにアクセスするには、アクセスが必要なすべてのスレッドで Realm インスタンスをインスタンス化する必要があります。 As long as you specify the same configuration, all realm instances will map to the same file on disk.
マルチスレッド環境で Realm を使用する際の重要なルールの 1 つは、 オブジェクトはスレッド定義であることです。他のスレッドで発生した Realm、コレクション、またはオブジェクトのインスタンスにはアクセスできません。 Realm のMultiversion Concurrency Control (MVCC)アーキテクチャでは、オブジェクトのアクティブなバージョンが常に多数存在する可能性があります。 スレッド制限により、そのスレッド内のすべての インスタンスが同じ内部バージョンになります。
スレッド間での通信が必要な場合は、ユースケースに応じていくつかのオプションがあります。
2 つのスレッド上のオブジェクトを変更するには、両方のスレッドでオブジェクトをクエリします。
React任意のスレッドで行われた変更に対応するには、Realm の 通知 を使用し 。
現在のスレッドの Realm インスタンス内の別のスレッドに発生した変更を確認するには、Realm インスタンスを更新します。
オブジェクトの高速で読み取り専用のビューを他のスレッドに送信するには、オブジェクトを「フリーズ」します。
アプリ内でオブジェクトの多くの読み取り専用ビューを保持して共有するには、Realm からオブジェクトをコピーします。
Realm または特定のオブジェクトのインスタンスを別のスレッドまたはアクター境界間で共有するには、Realm インスタンスまたはオブジェクトへのスレッドセーフな参照を共有します。 詳細については、「スレッドセーフリファレンスを渡す」を参照してください。
バックグラウンド スレッドで Realm を使用するための直列キューの作成
バックグラウンド スレッドで Realm を使用する場合は、シリアル キューを作成します。 Realm では、 global()
キューなどの同時キューで Realm の使用をサポートしていません。
// Initialize a serial queue, and // perform realm operations on it let serialQueue = DispatchQueue(label: "serial-queue") serialQueue.async { let realm = try! Realm(configuration: .defaultConfiguration, queue: serialQueue) // Do something with Realm on the non-main thread }
スレッド間で インスタンスを渡す
Realm
、 Results
、 List
、および管理対象Objects
のインスタンスは、スレッド定義です。 つまり、それらを作成したスレッドでのみ使用できます。 ただし、Realm は、あるスレッドで作成された インスタンスを別のスレッドにコピーできる「スレッドセーフ参照」と呼ばれるメカニズムを提供します。
送信可能な準拠
バージョン 10.20.0 の新機能: @スレッドセーフ ラッパーとスレッドセーフリファレンスがSendable
に準拠
Swift5.6 以降を使用している場合、 @Thread セーフ プロパティ ラッパー とスレッドセーフリファレンスの両方が送信可能に準拠してい ます。
@スレッドセーフ ラッパーの使用
バージョン 10.17.0 の新機能。
次のように、スレッド定義された インスタンスを別のスレッドに渡すことができます。
@ThreadSafe
プロパティ ラッパーを使用して、元のオブジェクトを参照する変数を宣言します。 定義では、@ThreadSafe
でラップされた変数は常に任意です。@ThreadSafe
でラップされた変数を他のスレッドに渡します。オプションと同様に、
@ThreadSafe
でラップされた変数を使用します。 参照先のオブジェクトが Realm から削除されると、参照元の変数は nil になります。
let realm = try! Realm() let person = Person(name: "Jane") try! realm.write { realm.add(person) } // Create thread-safe reference to person var personRef = person // @ThreadSafe vars are always optional. If the referenced object is deleted, // the @ThreadSafe var will be nullified. print("Person's name: \(personRef?.name ?? "unknown")") // Pass the reference to a background thread DispatchQueue(label: "background", autoreleaseFrequency: .workItem).async { let realm = try! Realm() try! realm.write { // Resolve within the transaction to ensure you get the // latest changes from other threads. If the person // object was deleted, personRef will be nil. guard let person = personRef else { return // person was deleted } person.name = "Jane Doe" } }
別のスレッド上のオブジェクトを操作する別の方法は、そのスレッドでそのオブジェクトを再度クエリすることです。 しかし、オブジェクトにプライマリキーがない場合、クエリは簡単ではありません。 @ThreadSafe
ラッパーは、プライマリキーがあるかどうかに関係なく、任意のオブジェクトで使用できます。
例
次の例では、関数パラメータで@ThreadSafe
を使用する方法を示しています。 これは、非同期に実行されたり、別のスレッドで実行されたりする可能性のある関数に役立ちます。
Tip
アプリがasync/await
コンテキストで Realm にアクセスする場合は、スレッド関連のクラッシュを回避するためにコードを@MainActor
でマークします。
func someLongCallToGetNewName() async -> String { return "Janet" } func loadNameInBackground( person: Person?) async { let newName = await someLongCallToGetNewName() let realm = try! await Realm() try! realm.write { person?.name = newName } } func createAndUpdatePerson() async { let realm = try! await Realm() let person = Person(name: "Jane") try! realm.write { realm.add(person) } await loadNameInBackground(person: person) } await createAndUpdatePerson()
スレッドセーフ参照の使用(レガシー Swift / Objective-C)
Realm Swift SDK バージョン 10.17.0 以前または Objective-C では、次のように、スレッド定義された インスタンスを別のスレッドに渡すことができます。
スレッドセーフ リファレンスをスレッド定義オブジェクトで初期化します。
他のスレッドまたはキューに参照を渡します。
Realm.resolve(_:)を呼び出して、他のスレッドの Realm の参照を解決します。 返されたオブジェクトを通常どおり使用します。
重要
ThreadSafeReference
は 1 回だけ解決する必要があります。 それ以外の場合、参照が解放されるまで、ソース邦土は固定されたままになります。 このため、 ThreadSafeReference
の有効期間は短くする必要があります。
let person = Person(name: "Jane") let realm = try! Realm() try! realm.write { realm.add(person) } // Create thread-safe reference to person let personRef = ThreadSafeReference(to: person) // Pass the reference to a background thread DispatchQueue(label: "background", autoreleaseFrequency: .workItem).async { let realm = try! Realm() try! realm.write { // Resolve within the transaction to ensure you get the latest changes from other threads guard let person = realm.resolve(personRef) else { return // person was deleted } person.name = "Jane Doe" } }
別のスレッド上のオブジェクトを操作する別の方法は、そのスレッドでそのオブジェクトを再度クエリすることです。 しかし、オブジェクトにプライマリキーがない場合、クエリは簡単ではありません。 プライマリキーがあるかどうかに関係なく、任意のオブジェクトでThreadSafeReference
を使用できます。 リストや結果でも使用することもできます。
ダウンタイムは、 ThreadSafeReference
にある程度のディレクティブを必要とすることです。 オブジェクトがバックグラウンド スレッドで存在しないようにするには、 DispatchQueue
内のすべての内容を適切にスコープ指定されたautoreleaseFrequency
でラップする必要があります。 したがって、次のように、ポインターレートを処理するための便宜的拡張を作成すると便利です。
extension Realm { func writeAsync<T: ThreadConfined>(_ passedObject: T, errorHandler: @escaping ((_ error: Swift.Error) -> Void) = { _ in return }, block: @escaping ((Realm, T?) -> Void)) { let objectReference = ThreadSafeReference(to: passedObject) let configuration = self.configuration DispatchQueue(label: "background", autoreleaseFrequency: .workItem).async { do { let realm = try Realm(configuration: configuration) try realm.write { // Resolve within the transaction to ensure you get the latest changes from other threads let object = realm.resolve(objectReference) block(realm, object) } } catch { errorHandler(error) } } } }
この拡張機能は Realm クラスにwriteAsync()
メソッドを追加します。 このメソッドは インスタンスをバックグラウンド スレッドに渡します。
例
メール アプリを作成し、バックグラウンドで読み取られたメールをすべて削除したいとします。 現在、2 行のコードでそれを実行できます。 閉じる はバックグラウンド スレッドで実行され、Realm と渡されたオブジェクトの両方の独自のバージョンを受け取ることに注意してください。
let realm = try! Realm() let readEmails = realm.objects(Email.self).where { $0.read == true } realm.writeAsync(readEmails) { (realm, readEmails) in guard let readEmails = readEmails else { // Already deleted return } realm.delete(readEmails) }
スレッド間で同じ Realm を使用
スレッド間で Realm インスタンスを共有することはできません。
スレッド間で同じ Realm ファイルを使用するには、各スレッドで異なる Realm インスタンスを開きます。 同じ構成を使用している限り、すべての Realm インスタンスがディスク上の同じ ファイルにマッピングされます。
Realm の更新
Realm を開くと、最新の成功した書込みコミットが反映され、更新される までそのバージョンのままになります。 つまり、Realm は次回の更新まで、別のスレッドで発生した変更を参照できません。 UI スレッド上の Realm(より正確には、任意のイベント ループ スレッド上の)は、そのスレッドのループの開始時に自動的に更新されます。 ただし、ループ スレッドに存在しない、または自動更新が無効になっている Realm インスタンスは、手動で更新する必要があります。
if (![realm autorefresh]) { [realm refresh] }
if (!realm.autorefresh) { // Manually refresh realm.refresh() }
凍結されたオブジェクト
スレッドで区切られたライブ オブジェクトはほとんどの場合正常に動作します。 ただし、一部のアプリ(リアクティブなイベントストリームベースのアーキテクチャに基づくものなど)では、UI スレッドで最終的に終了する前に、処理のために不変のコピーを多数のスレッドに送信する必要があります。 毎回深いコピーを作成するとコストが高くなるため、Realm ではライブ インスタンスをスレッド間で共有できません。 この場合、オブジェクト、コレクション、Realm をフリーズして解凍できます。
固定化により、特定のオブジェクト、コレクション、または Realm の不変のビューが作成されます。 固定されたオブジェクト、コレクション、または Realm は引き続きディスク上に存在するため、他のスレッドに渡す際に深くコピーする必要はありません。 スレッドの問題に関係なく、固定されたオブジェクトをスレッド間で自由に共有できます。 Realm を固定すると、その子オブジェクトも固定されます。
Tip
Swift アクターでスレッドセーフリファレンスを使用する
Realm は現在、Swift アクターでthaw()
を使用することをサポートしていません。 アクターの境界をまたがって Realm データを操作するには、固定されたオブジェクトの代わりにThreadSafeReference
を使用します。 詳細については、「スレッドセーフリファレンスを渡す」を参照してください。
凍結されたオブジェクトはライブではなく、自動的に更新されません。 実質的に、停止時にオブジェクトの状態のスナップショットです。 オブジェクトを解凍すると、固定されているオブジェクトのライブ バージョンが返されます。
// Get an immutable copy of the realm that can be passed across threads RLMRealm *frozenRealm = [realm freeze]; RLMResults *dogs = [Dog allObjectsInRealm:realm]; // You can freeze collections RLMResults *frozenDogs = [dogs freeze]; // You can still read from frozen realms RLMResults *frozenDogs2 = [Dog allObjectsInRealm:frozenRealm]; Dog *dog = [dogs firstObject]; // You can freeze objects Dog *frozenDog = [dog freeze]; // To modify frozen objects, you can thaw them // You can thaw collections RLMResults *thawedDogs = [dogs thaw]; // You can thaw objects Dog *thawedDog = [dog thaw]; // You can thaw frozen realms RLMRealm *thawedRealm = [realm thaw];
let realm = try! Realm() // Get an immutable copy of the realm that can be passed across threads let frozenRealm = realm.freeze() assert(frozenRealm.isFrozen) let people = realm.objects(Person.self) // You can freeze collections let frozenPeople = people.freeze() assert(frozenPeople.isFrozen) // You can still read from frozen realms let frozenPeople2 = frozenRealm.objects(Person.self) assert(frozenPeople2.isFrozen) let person = people.first! assert(!person.realm!.isFrozen) // You can freeze objects let frozenPerson = person.freeze() assert(frozenPerson.isFrozen) // Frozen objects have a reference to a frozen realm assert(frozenPerson.realm!.isFrozen)
固定されたオブジェクトを操作している場合、次のいずれかを実行しようとすると例外がスローされます。
固定された Realm で書込みトランザクションを開始する。
固定されたオブジェクトを変更します。
固定された Realm、コレクション、またはオブジェクトへの変更リスナーの追加。
isFrozen
を使用して、オブジェクトが固定されているかどうかを確認できます。 これは常にスレッドセーフです。
if ([realm isFrozen]) { // ... }
if (realm.isFrozen) { // ... }
凍結されたオブジェクトは、それを生成したライブ環境が開いている限り有効のままです。 したがって、すべてのスレッドが固定されたオブジェクトの処理を完了するまで、ライブ邦土を閉じないようにします。 ライブ Realm が閉じられる前に、固定された Realm を閉じることができます。
重要
固定オブジェクトのキャッシュについて
固定オブジェクトがキャッシュされすぎると、Realm ファイルのサイズに悪影響が及ぶ可能性があります。 「多すぎる」は、特定のターゲット デバイスと Realm オブジェクトのサイズによって異なります。 多数のバージョンをキャッシュする必要がある場合は、Realm から必要なものをコピーすることを検討してください。
凍結されたオブジェクトの変更
固定されたオブジェクトを変更するには、オブジェクトを解凍する必要があります。 あるいは、解凍されていない Realm でその をクエリしてから変更することもできます。 ライブ オブジェクト、コレクション、または Realm でthaw
を呼び出すと、それ自体が返されます。
オブジェクトまたはコレクションを解凍すると、参照する Realm も解凍されます。
// Read from a frozen realm let frozenPeople = frozenRealm.objects(Person.self) // The collection that we pull from the frozen realm is also frozen assert(frozenPeople.isFrozen) // Get an individual person from the collection let frozenPerson = frozenPeople.first! // To modify the person, you must first thaw it // You can also thaw collections and realms let thawedPerson = frozenPerson.thaw() // Check to make sure this person is valid. An object is // invalidated when it is deleted from its managing realm, // or when its managing realm has invalidate() called on it. assert(thawedPerson?.isInvalidated == false) // Thawing the person also thaws the frozen realm it references assert(thawedPerson!.realm!.isFrozen == false) // Let's make the code easier to follow by naming the thawed realm let thawedRealm = thawedPerson!.realm! // Now, you can modify the todo try! thawedRealm.write { thawedPerson!.name = "John Michael Kane" }
凍結されたコレクションへの追加
固定のコレクションに追加する場合は、追加するコレクションとオブジェクトの両方を解凍する必要があります。 この例では、固定された Realm 内の 2 つのオブジェクトをクエリします。
ドキュメント オブジェクトのListプロパティを持つ ロール オブジェクト
犬オブジェクト
パーソナルのドキュメント リスト コレクションにドキュメントを追加する前に、両方のオブジェクトを解凍する必要があります。 Realmが
固定されたオブジェクトをスレッド間で渡す場合も同じルールが適用されます。 一般的なケースとして、バックグラウンド スレッドで関数を呼び出して、UI をブロックする代わりに処理を実行する場合があります。
// Get a copy of frozen objects. // Here, we're getting them from a frozen realm, // but you might also be passing them across threads. let frozenTimmy = frozenRealm.objects(Person.self).where { $0.name == "Timmy" }.first! let frozenLassie = frozenRealm.objects(Dog.self).where { $0.name == "Lassie" }.first! // Confirm the objects are frozen. assert(frozenTimmy.isFrozen == true) assert(frozenLassie.isFrozen == true) // Thaw the frozen objects. You must thaw both the object // you want to append and the collection you want to append it to. let thawedTimmy = frozenTimmy.thaw() let thawedLassie = frozenLassie.thaw() let realm = try! Realm() try! realm.write { thawedTimmy?.dogs.append(thawedLassie!) } XCTAssertEqual(thawedTimmy?.dogs.first?.name, "Lassie")
Realm のスレッドモデルの詳細
Realm は マルチバージョン同時実行制御 (MVCC) により、安全で高速、ロックフリーの同時アクセスを提供します。 アーキテクチャ。
Gitとの比較と対比
Git のような分散バージョン管理システムに理解がある場合 、MVCC について直感的に理解しているかもしれません。Git の 2 つの基本要素は次のとおりです。
アトミックな書込みである コミット 。
ブランチはコミット履歴の異なるバージョンです。
同様に、Realm にはトランザクションの形式でアトミックにコミットされた書込み (write) があります。 Realm には、ブランチと同様に、常に多くの異なるバージョンの履歴があります。
フォークを通じて分散と相違をアクティブにサポートする Git とは異なり、Realm には常に最新バージョンが 1 つだけあり、常にその最新バージョンのヘッドに書込まれます。 Realm は以前のバージョンに書込むことはできません。 つまり、データは 1 つの最新バージョンの真実のバージョンに変換されます。
内部構造
Realm は B+ ツリー を使用して実装されています データ構造。最上位の ノードは Realm のバージョンを表します。子ノードは、そのバージョンの Realm 内のオブジェクトです。 Realm には最新バージョンへのポインターがあります。これは、Git が ヘッドコミット へのポインターを持っているのと同様です。
Realm はコピーオンライト手法を使用して 分離 を確保します と の 耐久性 。変更を加えると、Realm は書き込みのためにツリーの関連部分をコピーします。 その後、Realm は 2 つのフェーズで変更をコミットします。
Realm は変更をディスクに書き込み、成功を確認します。
次に、Realm は最新バージョンのポインターを新しく書込まれたバージョンを指すように設定します。
この 2 段階のコミット プロセスにより、書込み (write) が部分的に失敗した場合でも、ツリーの関連する部分のコピーに対して変更が行われていたため、元のバージョンは一切破損しないことが保証されます。 同様に、Realm のルートポインターは、新しいバージョンの有効性が保証されるまで、元のバージョンを指します。
例
次の図は、コミット プロセスを示しています。
Realm はツリーとして構成されています。 Realm には最新バージョン V1 へのポインターがあります。
書き込み時に、Realm は V1 に基づいて新しいバージョン V2 を作成します。 Realm は変更対象のオブジェクトのコピー(A 1 、C 1 )を作成しますが、変更されていないオブジェクトへのリンクは引き続き元のバージョン(B、D)を指します。
コミットを検証した後、Realm はポインターを新しい最新バージョン(V2)に更新します。 その後、Realm はツリーに接続されなくなった古いノードを破棄します。
Realm はメモリ マッピングなどのゼロコピー メソッドを使用してデータを処理します。 Realm から値を読み取る場合、ほぼそのコピーではなく、実際のディスク上の値が表示されます。 これがライブ オブジェクトの基盤です。 これは、ディスクへの書き込みが検証された後に、Realm ヘッダー ポインターを新しいバージョンを指すように設定できる理由でもあります。
概要
Realm では、次の 3 つのルールに従う場合、簡単かつ安全なマルチスレッド コードが有効になります。
読み取りをロックしないでください
バックグラウンド スレッドで書き込みを行う場合やDevice Syncを使用する場合に、UI スレッドでの書き込みを回避する
ライブ オブジェクトを他のスレッドに渡さないでください。
ユースケースごとにスレッド間でオブジェクトを共有する適切な方法があります。
Realm インスタンス内の他のスレッドに加えられた変更を確認するには、「ループ」スレッドに存在しない、または自動更新が無効になっている Realm インスタンスを手動で更新する必要があります。
リアクティブなイベントストリームベースのアーキテクチャに基づくアプリでは、オブジェクト、コレクション、Realm をフリーズして、シャットダウンしたコピーを処理用の別のスレッドに効率的に渡すことができます。
Realm のマルチバージョン同時実行制御(MVCC)アーキテクチャは、Git の と同様です。 Git と違い、Realm では各 Realm に対して正確な最新バージョンが 1 つだけです。
Realm は 2 つのステージでコミットされ、分離と耐久性を保証します。
送信可能タイプ、非送信タイプ、スレッド付きタイプ
Realm Swift SDK パブリック API には、次の 3 つの大きなカテゴリに分類されるタイプが含まれています。
送信可能
送信不可、およびスレッド定義なし
スレッドのみ
送信可能ではなく、スレッドが限定されない型をスレッド間で共有できますが、これらは同期する必要があります。
スレッド定義型は、固定されていない限り、分離コンテキストに制限されます。 同期を使用しても、これらのコンテキスト間でそれらを渡すことはできません。
送信可能 | Non-Sendable | スレッド構成 |
---|---|---|
任意の BSON | RMAppConfiguration | AnyRealmCollection |
AsyncOpen | RMFindOneAndModifyOptions | AnyRealmValue |
AsyncOpenSubscription | RMFindOptions | リスト |
RMAAPIKeyAuth | RMNetworkTransport | Map |
RMApp | RMRequest | MutableSet |
RLMAsyncOpenTask | RMResponse | プロジェクション |
RMchangeStream | RMSyncConfiguration | RRMArray |
RMCompensatedWriteInfo | RMSyncTimeoutOptions | RMchangeStream |
RLMCredentials | RRMDictionary | |
RMDecimal128 | RRMDictionary変更 | |
RLMEmailPasswordAuth | RM埋め込みオブジェクト | |
RMMaxKey | RMLinkingObjects | |
RLMMinKey | RMObject | |
RMMongoClient | RMPropertyCheck | |
RMMongoCollection | RLMRealm | |
RMMongoDatabase | RMResults | |
RMObjectId | RLMSection | |
RObjectSchema | RLMSectionedResults | |
RMProgressNotification | RLMSectionedResultschangeset | |
RMProgressNotificationToken | RMSet | |
RMProperty | RMSyncSubscription | |
RLMPropertyDescriptor | RMSyncSubscriptionSet | |
RPMProviderClient | Realm任意 | |
RLMpushClient | RealmProperty | |
RLMSschema | ||
RLMSortDescriptor | ||
RMSyncErrorActionToken | ||
RMSyncManager | ||
RRMSyncSession | ||
RMThreadセーフリファレンス | ||
RLMUpdateResult | ||
RLMuser | ||
RLMuserAPIKey | ||
RLMuserIdentity | ||
RLMuserProfile | ||
スレッドセーフ |