【问题标题】:RLMException "Realm at path ' ' already opened with different encryption key" after writeCopy(toFile:,encryptionKey:)在 writeCopy(toFile:,encryptionKey:) 之后 RLMException “路径 ' ' 的领域已经使用不同的加密密钥打开”
【发布时间】:2017-08-20 09:56:31
【问题描述】:


我正在尝试使用 writeCopy(toFile:, encryptionKey:) 更改我的 Realm 数据库的加密密钥,如下所示:

public static func updateEncryption(forNewKey newKey: String, withOldKey oldKey: String, completion: (() -> Void)) {

    let defaultURL = Backup.realmContainerURL
    let defaultParentURL = defaultURL.deletingLastPathComponent()
    let compactedURL = defaultParentURL.appendingPathComponent("default-compact.realm")

    let oldKeyData = oldKey.pbkdf2SHA256(keyByteCount: 64)
    let newKeyData = newKey.pbkdf2SHA256(keyByteCount: 64)

    let oldEncryptionConfig = Realm.Configuration(fileURL: Backup.realmContainerURL, encryptionKey: oldKeyData)

    autoreleasepool {
        do {
            let oldRealm = try Realm(configuration: oldEncryptionConfig)
            try oldRealm.writeCopy(toFile: compactedURL, encryptionKey: newKeyData)
            oldRealm.invalidate()
            try FileManager.default.removeItem(at: defaultURL)
            try FileManager.default.moveItem(at: compactedURL, to: defaultURL)
            completion()
        } catch {
            fatalError(error.localizedDescription)
        }
    }
}

之后,我正在使用以下方法在我的应用中重新加载数据:

public static func loadAll(withEncryptionKey encryptionKey: String) -> [Backup] {
    do {
        let key = encryptionKey.pbkdf2SHA256(keyByteCount: 64)
        let encryptionConfig = Realm.Configuration(fileURL: Backup.realmContainerURL, encryptionKey: key)
        let realm = try Realm(configuration: encryptionConfig)
        let allObjects = realm.objects(Backup.self).sorted(byKeyPath: "date", ascending: false)
        return allObjects.map({ $0 })
    } catch {
        fatalError(error.localizedDescription)
    }
}

所以我将loadAll(withEncryptionKey:) 函数与新密钥一起使用,但在let realm = try Realm(configuration: encryptionConfig) 应用程序在RLMRealm.mm 文件第347 行崩溃:@throw RLMException(@"Realm at path '%s' already opened with different encryption key", config.path.c_str()); 与控制台日志libc++abi.dylib: terminating with uncaught exception of type NSException
所以看起来writeCopy(toFile:, encryptionKey:) 没有更改encryptionKey,或者Realm 仍然看到旧的.realm 文件。但有趣的是,在重新打开我的应用程序后,loadAll(withEncryptionKey:) 使用新的加密密钥加载数据没有任何问题。
如何解决这个问题呢?如何更改加密密钥仍然可以使用应用程序?
非常感谢您的帮助。

【问题讨论】:

    标签: ios swift3 realm


    【解决方案1】:

    这取决于您调用loadAll() 方法的位置。也许您正在打电话给completion(),对吧?如果是这样,那么对旧 Realm 的引用还没有被释放,它仍然是打开的。

    就像从磁盘中删除/替换 Realm 文件一样,只有在您的应用程序当前没有打开 Realm 文件时,替换磁盘上的 Realm 文件才是安全的。

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

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

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

    这样做的原因是 Realm 维护一个打开文件的内存缓存,因此尝试打开已经打开的文件将导致返回对已打开文件的引用。这个打开的文件将继续引用磁盘上的原始文件,即使它已被替换。确保对 Realm 访问器对象的所有引用都已被清除意味着 Realm 将不会返回现有的打开文件,而是会从磁盘打开文件。

    换句话说,当您尝试替换 Realm 文件时,您必须确保没有引用 Realm 的访问器对象(RealmResultsThreadSafeReferenceObject 实例)。您还必须确保您做过的所有引用都已被释放。

    如果此时没有其他对 Realm 和 Realm 对象的引用,它将通过在 autorelease 块之外打开来成功,如下所示。

    autoreleasepool {
        do {
            let oldRealm = try Realm(configuration: oldEncryptionConfig)
            try oldRealm.writeCopy(toFile: compactedURL, encryptionKey: newKeyData)
            oldRealm.invalidate()
            try FileManager.default.removeItem(at: defaultURL)
            try FileManager.default.moveItem(at: compactedURL, to: defaultURL)
        } catch {
            fatalError(error.localizedDescription)
        }
    }
    
    loadAll(withEncryptionKey: ...)
    

    另一种可能更易于管理的方法是在您尝试重新打开恢复的文件时使用不同的路径。由于您正在访问磁盘上的不同路径,因此可以保证打开新文件。你仍然需要确保你没有对 Realm 的访问器对象的引用,否则你会得到新旧数据的奇怪混合,但确保访问器对象被释放并不那么重要。

    【讨论】:

    • 我删除了对我在writeCopy(toFile:, encryptionKey:) 之前拥有的所有 Realm 对象的所有引用,现在它工作得非常好。非常感谢你的帮助。 :)
    • 我在autoreleasepool 上添加了与领域相关的所有写入和更新,但我想用我的旧领域名称复制新领域(在你的情况下为“default-compact.realm”),一切都是好的,但是当我想创建一个与旧版本名称相同的领域时,我会收到类似您的错误的错误。
    猜你喜欢
    • 2016-05-13
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多