核心数据配置和重置数据



我在核心数据中遇到了一个烦人的错误,它导致我的一些最终用户出现了一些问题。一些背景:

我的数据模型包含两种配置,一种是CRM配置,另一种是Catalog配置。CRM配置具有与其他相关实体中的联系人/帐户等事物相关的实体。目录包含其他实体,如集合/产品等。

在用户首次登录后,他们被迫通过web服务同步两个存储。所有这些都很好。在应用程序的最新更新中,我提示用户需要再次同步目录数据。这会将它们发送回同步屏幕,以便它们可以再次同步目录。

问题就在这里。当他们执行同步时,我们的代码会从上下文中删除持久存储,删除.sqlite文件,然后将存储添加回持久存储。

代码:

- (void) resetStoreAtPath:(NSURL *)path withConfiguration:(NSString *)configuration
{
NSPersistentStoreCoordinator *coordinator = self.persistentStoreCoordinator;
//Get the store for this database.
NSPersistentStore *store = [coordinator persistentStoreForURL:path];
NSError *__autoreleasing error = nil;
if(store != nil)
{
if(![coordinator removePersistentStore:store error:&error])
{
//Log The Error
}
}
//Remove the file.  Even if it errors, try to add it, but log it.
error = nil;
if(![[NSFileManager defaultManager] removeItemAtURL:path error:&error])
{
//Log the error
}
error = nil;
[self addStoreWithConfiguration:configuration URL:path error:&error];
//Log Error
}

以上代码均无错误。使用iFunBox,我甚至可以浏览应用程序并检查文件系统,并且可以毫无问题地重新创建.sqlite文件。

完成后,我们将再次同步目录数据。奇怪的是,偶尔(不是每次),核心数据会决定将目录配置实体放入CRM数据库,而不是新的目录数据库!

稍后在应用程序中,当用户浏览目录时,目录数据不再显示。

我已经从设备上取下.sqlite文件,以确认目录数据在crm.sqlite数据库中。并且catalog.sqlitedb中没有任何数据行。发生这种情况有什么特别的原因吗?有更好的方法吗?

EDIT:这是另一种读取方法。

- (NSPersistentStore *) addStoreWithConfiguration:(NSString *)configuration URL:(NSURL *)url error:(NSError **)error
{
NSDictionary *options = @{ NSSQLitePragmasOption: @{@"journal_mode": @"DELETE"}, NSMigratePersistentStoresAutomaticallyOption: @YES, NSInferMappingModelAutomaticallyOption: @YES };
NSPersistentStore *store = [self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:configuration URL:url options:options error:error];
if(store == nil)
{
//Something went wrong. Log it. Remove File, try to readd.
[[NSFileManager defaultManager] removeItemAtURL:url error:nil];
store = [self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:configuration URL:url options:options error:error];
if(store == nil)
{
//Something is dreadfully wrong, log and throw.
[NSException raise:@"NSInternalInconsistencyException" message:@"After two attempts, we were unable to create the database.  User may need to uninstall their app to continue." parameters:nil];
}
}
return store;
}

我只是删除我需要的存储,在本例中是目录存储。

第2版:关于您的问题。。。

  1. 删除代码发生在响应按钮按下的主线程上。然后,我们启动一个下载zip文件的操作,完成后,我们在后台操作中处理zip文件。后台进程使用一个专用队列上下文,该上下文保存后将合并到主上下文上。

  2. 我认为锁定在这种情况下不会有帮助,因为目录的数据在商店重置后会保存很多。不过尝试一下也无妨。

也许描述问题是如何发生的可能会有所启发。

  1. 用户是以前的版本(在本例中为1.0.3)
  2. 系统会提示用户有可用的新版本
  3. 用户安装新版本并运行应用程序
  4. 出现提示,用户选择"是">
  5. 新屏幕显示模式。用户按下按钮开始处理
  6. 一旦过程完成,模态就会消失。目录数据显示在前面和中间,但似乎是缓存的
  7. 关闭并从内存中清除应用程序,然后重新启动,所有目录数据现在都不见了

我可以通过执行以下操作来防止这种情况发生:

  1. 按照上面的步骤1-4进行操作
  2. 此时,从内存中清除应用程序并重新启动。启动下载等过程是有效的,目录数据现在可以通过多次重新启动随时可见

我想知道在这一点上,整个核心数据堆栈的重新创建是否会解决这个问题。我真的很想避免,因为这似乎完全没有必要。

此错误发生在iOS 7下吗?如果是这样,则说明您遇到了日志文件的问题。苹果在iOS 7中更改了核心数据下的SQLite配置,以使用日志文件。如果要删除sqlite文件,那么还需要检查并删除日志文件。它们将具有与您的主sqlite文件相同的核心文件名。

另一种选择是关闭日志记录,这样你就可以获得与iOS 7之前相同的行为。

更新1

磁盘上是否仍有日志文件,或者您是在干净的环境中出现这种行为的?

您的-addStore...方法是什么样子的?

你是不是同时把两家商店都撤了?

你的投递商店代码是什么样子的?

更新2

您似乎没有检查从-addPersistentStore...返回的错误。你有错吗?我可以很容易地看到这样一种情况,其中一个-addPersistentStore...调用失败,然后磁盘上只有一个存储区,所以Core Data将数据写入其中

建议您始终检查错误,即使只是将其记录到磁盘。我会更进一步,推荐一个NSAssert,商店将返回!nil

更新3

除去剩下的,我的思绪转向了线程。你什么时候删除这个商店?在删除和添加之间是否有可能触发保存?

这是在背景线索上开火吗?

如果在这个过程的顶部锁定NSPersistentStoreCoordinator,然后在底部解锁,会发生什么?

当您使用配置时,Core Data实际上会将所有表放在所有存储中,即使这些表没有被使用。如果我们遇到竞争条件,我可以看到核心数据可能会写入"错误"的存储,因为在那个时刻它是唯一的存储。锁定PSC将检验这一理论。

更新4

重建整个核心数据堆栈并没有那么昂贵,您已经在做昂贵的部分了。这绝对闻起来像是与该商店在错误的时间不可用的碰撞,但如果我面前没有很难证明/反驳的应用程序。如果你的应用程序结构足够好,可以重建堆栈,我同意这样做是你的最短路径。在这种情况下,从轨道发射核武器是可行的策略。

Marcus无疑让我走上了正确的道路,所以谢谢你。

最后,刷新整个核心数据堆栈解决了我的问题,尽管我有点困惑为什么这是必要的。

我必须说,这是持久存储协调器在使用多个配置和重置存储时没有将正确的实体插入正确的存储的潜在问题。

最新更新