【问题标题】:Is it safe to perform Realm local migration in background thread?在后台线程中执行 Realm 本地迁移是否安全?
【发布时间】:2021-12-10 06:12:32
【问题描述】:

我们正在优化应用启动时间,其中一个巨大的减速来自领域初始化。

我们有这段代码在didFinishLaunchingWithOptions期间被调用

RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
config.schemaVersion = kCurrentSchema;
config.migrationBlock = /* migration task */;
config.fileURL = fileUrl;
config.encryptionKey = encryptionKey;
    
return config;

在初始化期间,我们尝试使用此配置至少创建一次RLMRealm,以查看我们是否能够打开领域。如果打开失败,我们将使用NSFileManager 执行操作以删除 Realm 文件夹并重新开始。 一切完成后,我们设置

[RLMRealmConfiguration setDefaultConfiguration:config];

我的问题是

  1. 能否在后台线程中移动创建RLMRealmConfiguration和初始化RLMRealm的整个过程?
  2. 迁移完成后,我们能否将后台线程中的RLMRealmConfiguration create 分派给主线程,以便在主线程上调用setDefaultConfiguration?。
  3. 迁移成功后dispatch到主线程调用[RLMRealmConfiguration setDefaultConfiguration:config];,后台线程的迁移结果是写入文件还是内存中?如果我在setDefaultConfiguration 之后直接在主线程上创建另一个RLMRealm。我可以访问迁移的版本吗?我知道RLMThreadSafeReference,但只有在我们知道如何访问时才能使用。在我的情况下,设置后我们会有太多的访问权限。

【问题讨论】:

  • 为了清楚起见,这是一个本地领域,对吧?不是同步领域
  • @Jay 是的,仅限本地领域

标签: ios realm


【解决方案1】:

创建一个新的 RLMRealmConfiguration 和调用 setDefaultConfiguration 是微不足道的操作。它们都不会触发迁移。

在您打开领域之前,迁移不会运行。

有几种方法可以使迁移在后台线程中运行。我将向您展示 Swift 中最简单的方法。您必须将此代码翻译成 Objective-C:

enum Migrator {

    static func migrate(completion: @escaping () -> Void) {

        let queue = DispatchQueue(label: "migrator.awesome")

        let configuration = Realm.Configuration(
            schemaVersion: 13,
            migrationBlock: { migration, oldSchemaVersion in
                if oldSchemaVersion < 1 {
                    // ...
                }
                if oldSchemaVersion < 2 {
                    // ...
                }
                if oldSchemaVersion < 3 {
                    // ...
                }
                // ...
            }
        )

        Realm.asyncOpen(configuration: configuration, callbackQueue: queue) { result in

            Realm.Configuration.defaultConfiguration = configuration

            switch result {
            case .failure(let error):
                print("error", error)
            case .success(let realm):
                print("realm", realm)
                break
            }
            completion()
        }
    }
}

asyncOpen 将打开一个领域并在指定队列上运行迁移。在您的情况下,您想传入后台队列。迁移运行后,asyncOpen 将在您指定的同一队列上调用指定的回调。

迁移完成后,您可以切换回主线程并执行您需要执行的任何操作。是的,在后台线程中迁移的数据将提供给主线程。

enum RealmInitializer {
    static func initializeRealm() {
        Migrator.migrate {
            DispatchQueue.main.async {
                readAndWriteRealmData()
            }
        }
    }
}

【讨论】:

  • 我很喜欢这个答案,直到我看到这个 Realm.asyncOpen(configuration: 看起来你正在尝试打开一个同步的 MongoDB 领域 - Open Realm Sync。 Realm Sync 不支持migrations - 这仅适用于本地领域,因此必须使用let localRealm = try! Realm() 打开领域。如果我看的不正确,请告诉我。
  • @Jay 很高兴见到你。我建议您查看docs。它特别提到了处理耗时的迁移。但您部分正确,因为它确实也适用于同步领域。
  • 该方法Realm.asyncOpen 是我们想要的,感谢示例和澄清!
猜你喜欢
  • 2011-04-17
  • 2016-04-24
  • 2012-06-19
  • 1970-01-01
  • 2021-12-05
  • 1970-01-01
  • 2013-12-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多