【问题标题】:Realm crash when invalidating while updating更新时失效时领域崩溃
【发布时间】:2017-08-14 21:16:37
【问题描述】:

当我尝试删除数据库而我仍在其他线程中运行更新事件时,Realm 经常崩溃。

崩溃是:

2017-08-14 18:07:56.289 App Staging[28264:7828070] *** Terminating app due to uncaught exception 'RLMException', reason: 'Can only add, remove, or create objects in a Realm in a write transaction - call beginWriteTransaction on an RLMRealm instance first.'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010c67fb0b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x000000010c0e4141 objc_exception_throw + 48
    2   Realm                               0x0000000108095f96 _ZL27RLMVerifyInWriteTransactionP8RLMRealm + 86
    3   Realm                               0x000000010809710a RLMCreateObjectInRealmWithValue + 138
    4   Realm                               0x00000001080820af +[RLMObject createOrUpdateInRealm:withValue:] + 607
    5   App Staging                         0x0000000107497ae0 +[RealmRoundable createOrUpdateInRealm:withMemberResponse:] + 400
    6   App Staging                         0x0000000107497916 +[RealmRoundable createOrUpdateWithMemberResponse:] + 118
    7   App Staging                         0x000000010742a0a0 +[RealmStaff createOrUpdateInRealm:withResponse:inCareProvider:] + 352
    8   App Staging                         0x000000010742ab92 +[RealmStaff createOrUpdateInRealm:withStaff:inCareProvider:] + 514
    9   App Staging                         0x000000010742a94b +[RealmStaff createOrUpdateStaff:inCareProvider:] + 139
    10  App Staging                         0x000000010733b803 -[StaffRoundableTableViewController updateRoundables:fromDataLoader:inCareProvider:] + 131
    11  App Staging                         0x000000010747ae3a __54-[RoundableTableViewController dataLoaderDidLoadData:]_block_invoke.324 + 122
    12  Realm                               0x00000001081d01a6 -[RLMRealm transactionWithBlock:error:] + 86
    13  Realm                               0x00000001081d010e -[RLMRealm transactionWithBlock:] + 62
    14  App Staging                         0x000000010747ab0d __54-[RoundableTableViewController dataLoaderDidLoadData:]_block_invoke + 765
    15  libdispatch.dylib                   0x000000010e15c4a6 _dispatch_call_block_and_release + 12
    16  libdispatch.dylib                   0x000000010e18505c _dispatch_client_callout + 8
    17  libdispatch.dylib                   0x000000010e164dcd _dispatch_queue_override_invoke + 1321
    18  libdispatch.dylib                   0x000000010e166ec4 _dispatch_root_queue_drain + 634
    19  libdispatch.dylib                   0x000000010e166bef _dispatch_worker_thread3 + 123
    20  libsystem_pthread.dylib             0x000000010e51c5a2 _pthread_wqthread + 1299
    21  libsystem_pthread.dylib             0x000000010e51c07d start_wqthread + 13
)
libc++abi.dylib: terminating with uncaught exception of type NSException

当我打电话时会发生这种情况:

[RealmManager deleteRealm];

具体实现为:

+ (void)deleteRealm
{
    @autoreleasepool {
        [[RLMRealm defaultRealm] invalidate];
        // Hack to force Realm to clear cache because config is cached and crashes eventually because it detects encryption key has changed
        SUPPRESS_UNDECLARED_SELECTOR_WARNING([[RLMRealm class] performSelector:@selector(resetRealmState)]);
    }

    NSFileManager *manager = [NSFileManager defaultManager];
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
    NSArray<NSURL *> *realmFileURLs = @[
                                        config.fileURL,
                                        [config.fileURL URLByAppendingPathExtension:@"lock"],
                                        [config.fileURL URLByAppendingPathExtension:@"log_a"],
                                        [config.fileURL URLByAppendingPathExtension:@"log_b"],
                                        [config.fileURL URLByAppendingPathExtension:@"note"],
                                        [[config.fileURL URLByDeletingLastPathComponent] URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.realm.management", [[config.fileURL URLByDeletingPathExtension] lastPathComponent]]]
                                        ];
    for (NSURL *URL in realmFileURLs) {
        NSError *error = nil;
        [manager removeItemAtURL:URL error:&error];
        if (error) {
            // handle error
            DDLogError(@"Error deleting realm file - %@", error);
        }
    }
}

我的问题是:有没有办法在运行此代码之前停止所有 Realm 操作

【问题讨论】:

    标签: objective-c crash realm


    【解决方案1】:

    来自 Realm 在Deleting Realm Files 上的文档:

    因为除非绝对需要,否则 Realm 会避免将数据复制到内存中,因此 Realm 管理的所有对象都包含对磁盘上文件的引用,并且必须在文件可以安全删除之前解除分配。这包括从 Realm 读取(或添加到)的所有对象、所有 RLMArrayRLMResultsRLMThreadSafeReference 对象,以及 RLMRealm 本身。

    实际上,这意味着删除 Realm 文件应该在应用程序启动时在您打开 Realm 之前完成,或者仅在显式自动释放池中打开 Realm 之后完成,这样可以确保所有 Realm 对象都已被删除。已解除分配。

    在另一个线程上仍在访问文件时删除文件会导致各种问题。也可以调用 Realm 的私有方法,例如 +[RLMRealm resetRealmState]。我强烈建议不要做这两件事。

    您可能会以稍微不同的方式处理此问题,具体取决于从磁盘中删除 Realm 文件的动机。如果您可以分享有关您的用例的更多信息,我或许可以提供更具体的建议。

    例如,您可以跟踪您的后台线程是否正在积极地使用 Realm,并且只有在它们空闲时才将其删除。但是,您需要非常小心,以确保在删除文件时已删除所有对 Realm 的引用,否则您最终可能会通过已打开的文件句柄继续访问现在已删除的文件。

    或者,您可以为新的 Realm 文件生成一个新的、唯一的路径,而不是立即删除 Realm 文件。然后,当您确定应用程序的其余部分未使用它们时,您将删除未使用的 Realm 文件(下一次启动是实现此目的的一种非常可靠的方法,或者将其绑定到应用程序生命周期中的点您知道无法再访问旧状态)。这将是我的偏好,因为在使用文件时不可能删除它。它也非常适合许多具有注销用户概念的应用程序,因为每个用户的不同 Realm 路径是一个相对容易掌握的概念。

    【讨论】:

    • 当用户结束会话时,出于 HIPAA 合规性的原因,我们会删除领域。我们曾经使用加密核心数据来做到这一点,但现在我们正在向 Realm 进行大规模迁移,为了通过审计,我们必须删除数据库的所有痕迹。
    • 会不会使用加密的Realm,在会话结束时丢弃加密密钥,然后再清理文件是否满足要求?
    • 我会试着卖掉它。现在我可以通过在删除领域文件之前运行它来避免崩溃。
    • [[RLMRealm defaultRealm] beginWriteTransaction]; [[RLMRealm defaultRealm] deleteAllObjects]; [[RLMRealm defaultRealm] commitWriteTransaction];
    猜你喜欢
    • 2020-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多