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”),一切都没问题,但当我想创建一个与旧版本名称相同的领域时,我会得到一个类似于您的错误的错误。