Core Data SQLite 存储在更新后变为只读

Posted

技术标签:

【中文标题】Core Data SQLite 存储在更新后变为只读【英文标题】:Core Data SQLite store becomes readonly after update 【发布时间】:2014-06-04 16:15:09 【问题描述】:

我有报告说用户在通过 App Store 更新后无法使用 ios 应用程序,因为 Core Data 使用的 SQLite 数据库显然变成了只读的。这发生在保存在应用程序包的Documents 文件夹中的读/写持久存储中。

每个用户首​​次登录应用时,都会从头开始创建持久存储。其内容通过自定义渐进式托管对象模型迁移进行维护。碰巧的是,最新版本没有要执行的迁移,因此持久性存储应该在应用启动后就可以打开了。

我们从用户那里收到的错误是相同的,它发生在升级完成后启动过程的早期。 NSError 对象的 userInfo 就是我们在这种情况下捕获的:

NSSQLiteErrorDomain = 264;
NSUnderlyingException = "error during prepareSQL for SQL string 'SELECT Z_VERSION, Z_UUID, Z_PLIST FROM Z_METADATA' : attempt to write a readonly database";

由于我们记录的内容和管理日志文件的方式存在一些缺陷,我无法准确知道在打开持久存储的过程中何时会发生这种情况。我们正在使用UIManagedDocument,但没有通过 iCloud 共享任何文档。我假设错误是在打开现有文档包时发生的,但即使这是一个猜测。 (因此,我对未来版本的日志记录进行了改进。)

我最接近重现错误的方法是在 iOS 模拟器应用程序安装的文档包中的 persistentStore-shm 文件上使用文件权限 0444,然后使用 sqlite3 命令行尝试 SELECT 语句界面。这种情况下的错误是SQLITE_CANTOPEN 而不是SQLITE_READONLY,所以我可能在关于文件权限的理论方面没有走上正轨。据我所知,UIManagedDocument 无论如何都会自动修复文件权限。

我想知道是否有人经历过类似的行为?如果是这样,有什么方法可以恢复,让用户不必经历重新创建本地数据存储的过程?

【问题讨论】:

您是否正在使用保存在捆绑包中的数据库? 是否有人已经在使用数据库(读写),而您尝试使用它(只读由于较早的读写)? @washloops 该数据库不包含在捆绑包中。它是在用户与应用交互并从服务器检索内容时构建的。 @JOM 据我所知,只有操作系统可以与数据库交互。我最早的怀疑是 App Store 升级过程可能是一个因素,但我想不出一种方法来证明这一点。 【参考方案1】:

将您自己的描述符添加到选项中以(重新)启用历史跟踪...

在作为容器设置的一部分加载商店之前执行此操作:

let container = NSPersistentContainer(name: "YourDataStoreIdentifier" )
        
let description = container.persistentStoreDescriptions.first
// This allows a 'non-iCloud' sycning 
// container to keep track of changes 
// as if it was an iCloud syncing container

description?.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
        
container.loadPersistentStores(...

【讨论】:

这救了我。很高兴知道。【参考方案2】:

我遇到了这个问题,这很令人费解。原来,预写日志的索引 -shm 文件已被清除,需要重建。这似乎是在将持久存储加载到 NSPersistentStoreCoordinator 时自动发生的。

如果你在这样做之前调用+[NSPersistentStore metadataForPersistentStoreWithURL:error:],我正在这样做,你会得到你报告的错误,返回值将是nil

因此,对我有用的解决方案是尝试检索元数据,如果失败并出现错误,则将持久性存储加载到 NSPersistentStoreCoordinator 以重建 -shm。之后,调用metadataForPersistentStoreWithURL:error: 将返回元数据而不会遇到只读错误。

【讨论】:

这真的很有趣。在打开商店之前有一种方法来检测是否需要重建 WAL 索引正是我所需要的。我会尽快调查这个程序。谢谢!

以上是关于Core Data SQLite 存储在更新后变为只读的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Core Data SQLite 存储不能从 OS X 10.8 向后兼容到 10.4?

Core Data (SQLite) 手动迁移是事务性的吗?

在 NSLibraryDirectory 或 NSApplicationSupportDirectory 中存储 Core Data Sqlite?

由于继承缺陷,Core Data sqlite 存储单表?

在 Iphone 中使用 Core Data 从 SQLite 数据库中获取主键值

iPhone 和 Apple Watch 应用之间的 Core Data SQLite 存储更改通知