核心数据 - 删除持久存储时出现死锁

Posted

技术标签:

【中文标题】核心数据 - 删除持久存储时出现死锁【英文标题】:Core Data - deadlock while removing persistent store 【发布时间】:2013-01-19 17:15:52 【问题描述】:

是否有一种安全的方法可以在应用程序中删除持久存储(并创建一个新存储),而其他线程正在使用与被删除的存储关联的 NSManagedObjectContext?我曾尝试锁定 NSPersistentStoreCoordinator 并在操作结束后解锁它,但它没有帮助 - 我所有的尝试都导致了死锁。它总是发生在这一行(在主线程上执行):

[self.persistentStoreCoordinator removePersistentStore: store error: &error];

【问题讨论】:

你能发布所涉及线程的堆栈跟踪图像吗? 你使用的是线程限制还是队列限制?你在使用父子上下文吗? 你首先应该确定为什么你想这样做,看看你是否能想出另一个解决方案。在大多数情况下,最好只拆除整个堆栈,并确保任何持有托管对象的东西都监听有关堆栈消失的通知,以便它们释放它们的引用。您正在尝试做的事情类似于从摩天大楼中拆除一楼,并期望一切都保持原来的样子。 【参考方案1】:

这个我没试过,但是从docs on mocsetPersistentStoreCoordinator:...

协调器提供托管对象模型和句柄 持久性。请注意,多个上下文可以共享一个协调器。

如果 coordinator 为 nil,此方法会引发异常。如果你想 “断开”上下文与其持久存储协调器的连接,您 应该简单地将所有对上下文的强引用设置为 nil 并且 让它正常释放。

这表明删除 psc 的安全方法是首先让每个带有 moc 的线程释放它(在 ARC 中对它的引用为零),然后执行removePersistentStore:

【讨论】:

所以您认为存在这个问题是因为存在需要解除分配的流氓上下文? 是的,估计不止一个,等待对方解封。 调试建议:创建NSManagedObjectContext的自定义子类。让它更新全局字典,其中包含从未保留的NSValuesetPersistentStoreCoordinator:...dealloc 等上的持久存储协调器的映射,可能还有一些其他信息,以允许正确识别上下文。要识别流氓,请检查字典。 是的,我同意需要有一种方法来获取属于协调器的所有上下文。我喜欢你使用未保留的 NSValue 集合的想法。【参考方案2】:

我会尝试使用此处描述的方法(Parent/Child Contexts 部分): Multi-Context CoreData

基本上,您的 PSC 只有一个与之关联的 MOC(父 MOC)。其他线程有自己的 MOC,它们的parentContext 设置为主 MOC(与 PSC 关联的那个)。

那么你可以试试这样的:

// Save each child MOC
for (NSManagedObjectContext *moc in self.someChildMOCs)

   [moc performBlockAndWait:^

       // push to parent
       NSError *error;
       NSAssert([moc save:&error]);
       moc.parentContext = nil;
   ];


// Save parent MOC to disk
[self.mainMOC performBlockAndWait:^
   NSError *error;
   NSAssert([mainMOC save:&error]);
];

[self.persistentStoreCoordinator removePersistentStore:store error:&error];
mainMOC.persistentStoreCoordinator = nil;

【讨论】:

【参考方案3】:

有没有一种安全的方法来删除应用程序中的持久性存储(并创建一个新的),而其他线程正在使用与被删除的存储关联的 NSManagedObjectContext?

它应该是安全的如果你可以保证没有活动的托管对象会尝试访问持久存储。这部分至关重要:您必须确保没有任何类型的活动对象已从旧持久存储加载或以其他方式与旧持久存储相关联。

您可以通过重置每个托管对象上下文来做到这一点:

[managedObjectContext performBlockAndWait:^
    [managedObjectContext reset];
];

每个托管对象上下文完成此操作后,您可以删除持久存储。

请注意,如果您有任何从这些上下文中获取的托管对象,则必须立即处置它们,而不读取或写入属性值或以任何方式使用它们。这些对象可能出于各种原因需要使用上下文,但在调用reset 之后,上下文不再了解它们。立即摆脱它们(最好是在调用 reset 之前),因为它们是定时炸弹,等着你一碰就炸毁你的应用程序。

【讨论】:

以上是关于核心数据 - 删除持久存储时出现死锁的主要内容,如果未能解决你的问题,请参考以下文章

SQLException : 处理 2 个不同的表时出现死锁错误

使用 swinject 注册具有关联类型的协议时出现问题

在 Windows 中的 Docker 中托管 Kafka 时删除主题时出现异常

使用 Rownum 时出现数据库死锁?

同时执行UPDATE时出现死锁

数据库表操作时出去死锁或卡主,最好的解决方法。。。。