Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/119.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios RLMEException“;路径'处的领域';已使用不同的加密密钥打开”;写入复制后(toFile:,encryptionKey:)_Ios_Swift3_Realm - Fatal编程技术网

Ios RLMEException“;路径'处的领域';已使用不同的加密密钥打开”;写入复制后(toFile:,encryptionKey:)

Ios RLMEException“;路径'处的领域';已使用不同的加密密钥打开”;写入复制后(toFile:,encryptionKey:),ios,swift3,realm,Ios,Swift3,Realm,我正在尝试使用writeCopy(toFile:,encryptionKey:)更改域数据库的加密密钥,如下所示: public static func updateEncryption(forNewKey newKey: String, withOldKey oldKey: String, completion: (() -> Void)) { let defaultURL = Backup.realmContainerURL let defaultParentURL


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

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(配置:encryptionConfig)
应用程序崩溃在
RLMRealm.mm
文件第347行:
@throw RLMException(@“路径“%s”处的域已使用不同的加密密钥打开”,config.path.c_str())带有控制台日志
libc++abi.dylib:以NSException类型的未捕获异常终止

因此它看起来像是
writeCopy(toFile:,encryptionKey:)
没有更改encryptionKey,或者Realm仍然看到旧的.Realm文件。但有趣的是,在重新打开我的应用程序后,
loadAll(withEncryptionKey:)
使用新的加密密钥加载数据,没有任何问题。
如何解决这个问题?如何更改加密密钥并仍然能够使用应用程序?

非常感谢您的帮助。

这取决于您在哪里调用
loadAll()
方法。也许您正在调用
completion()
,对吗?如果是这样的话,那么此时对旧领域的引用还没有被释放,它仍然是开放的

与从磁盘删除/替换领域文件非常相似,只有在应用程序当前未打开领域文件的情况下,才可以安全地替换磁盘上的领域文件

来自以下领域的Realm文档:

由于Realm避免将数据复制到内存中(绝对需要时除外),因此由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的访问器对象的引用,因为否则您将得到新旧数据的奇怪混合,但确保访问器对象被解除分配并不是那么重要。

我删除了对以前所有Realm对象的引用
writeCopy(toFile:,encryptionKey:)
现在它的效果非常好。非常感谢您的帮助。:)我在
autoreleasepool
上添加了与领域相关的所有写入和更新,但我想用我的旧领域名称复制新领域(在您的例子中是“default compact.realm”),一切都没问题,但当我想创建一个与旧版本名称相同的领域时,我会得到一个类似于您的错误的错误。