【问题标题】:Core Data executeFetchRequest throws NSGenericException (Collection was mutated while being enumerated)核心数据 executeFetchRequest 抛出 NSGenericException(枚举时集合发生了变异)
【发布时间】:2011-03-17 22:18:43
【问题描述】:

我正在使用 Core Data 开发 iPhone 应用程序。所有用户数据都应与我们的服务器同步。为此,我创建了一个 NSOperation 的子类,它从我们的 Web 服务加载新数据并创建相应的托管对象。为了维护它们之间的关系,每个对象都使用一个 remoteID(它是关系服务器数据库的主键)传输。

假设有两个托管对象:Department > Employee。同步工作如下:

  1. 从服务器加载所有部门。对于每个部门:创建一个部门对象并设置其 remoteID。

  2. 从服务器加载所有员工。对于每个员工:创建 Employee 对象,获取相关部门(通过 remoteID)并将其分配给员工。

获取部门会导致以下异常:

*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x69c8a10> was mutated while being enumerated.<CFBasicHash 0x69c8a10 [0x2d6d380]>{type = mutable set, count = 1424, 
entries => <A list of all newly created entities>

*** Call stack at first throw:
0 CoreFoundation  0x02d04919 __exceptionPreprocess + 185
1 libobjc.A.dylib 0x02e525de objc_exception_throw + 47
2 CoreFoundation  0x02d043d9 __NSFastEnumerationMutationHandler + 377
3 CoreData        0x026225d0 -[NSManagedObjectContext executeFetchRequest:error:] + 4400
4 myApp           0x00059de4 +[AppFactory departmentWithRemoteID:inManagedObjectContext:] + 259

不是每次都抛出异常。将代码移至主线程即可解决问题。我不知道出了什么问题。我在同步线程中创建了一个新的 NSManagedObjectContaxt,并通过它的 NSManagedObjectID 传递了所有托管对象。

有什么想法吗?

【问题讨论】:

    标签: iphone core-data


    【解决方案1】:

    我遇到了同样的问题... 它已解决,因为我使用的是在后台线程的主线程上创建的 managedObjectContext。 解决方案是在后台线程上创建一个不同的 ManagedObjectContext,并使用常规的 persistentStoreCoordinator ... 之后效果很好!

    【讨论】:

    • 是否有任何后台线程工作? stackoverflow.com/a/3448089/6839908 根据那里的讨论,需要在将使用它们的线程上创建后台上下文。我有点困惑,因为当我们要创建上下文时,将在其中使用上下文的线程还不存在,是吗?
    【解决方案2】:

    错误“someCollection was mutated while being enumerated”是由更改可变集合(即数组、字典、集合等)引起的,而枚举器正在逐步遍历它。由于您无法枚举移动目标,因此会触发错误。

    在这种情况下,错误很可能是由于试图在主线程上枚举部门的员工关系引起的,例如用于在 tableview 中显示,而后台线程同时将员工添加到关系中。

    确实解决了这个问题,您必须在合并来自后台线程的更改时冻结 UI。对于 tableviews,在 tableview 控制器中具有正确实现的委托方法的获取结果控制器 (NSFetchedResultsController) 将很好地处理该问题。

    重要的是在合并新数据之前将beginUpdates 发送到tableview。这将告诉表它的底层数据结构正在发生变化,因此它不会尝试重绘自己。合并完成后,将endUpdates 发送到tableview 以使其显示新信息。

    【讨论】:

    • 我想到了这类问题,因此我创建了两个 NSManagedObjectContexts(一个用于主线程,一个用于同步线程)。这不是一个有效的解决方案吗?我还将 NSFetchedResultsController 用于我的表格视图。
    • 是的,但是当您合并上下文所做的更改时,它们会触发更新以反映另一个上下文所做的更改。 FRC 委托方法让您告诉 UI 在处理这些更改时冻结。然后解冻 UI 并让它显示更新。这个过程通常对用户是不可见的。
    • 应用程序在我将更改合并到主线程之前崩溃。
    • 我应该补充一点,如果您不冻结 tableview,它可能会在其数据不断变化时尝试重绘自己。这会导致它接收到错误数量的部分和行,从而导致间歇性崩溃。
    • 发生崩溃是因为您正在获取某个关系中的一些对象,同时您正在改变持有该关系的集合。可能是当您在将员工添加到部门关系时为每个员工获取部门。将部门添加到员工会自动将该员工添加到部门。根据您对这一切的具体编码方式,它们可能会发生冲突。
    【解决方案3】:

    在我的脑海中浮现:“同步”线程是否在主线程上迭代时将新对象添加到 Department 集合中?

    通常,当您在枚举集合的同时修改集合时,会发生这种类型的异常。在多线程场景中,这也可能意味着您的集合是在没有适当线程同步的情况下同时枚举和更新的。

    【讨论】:

    • 对我来说,这可能是此类问题的原因。解决这些问题的最佳方法是什么,你知道@octy吗?
    • Adeem,在链接的讨论中为您的问题提供了一个很好的答案:stackoverflow.com/questions/3364769/…
    【解决方案4】:

    我有同样的问题。您可以使用锁定、解锁接收器。我解决了这个问题。

    【讨论】:

      猜你喜欢
      • 2015-05-23
      • 2011-03-27
      • 1970-01-01
      • 2012-05-11
      • 2016-11-20
      • 2017-06-28
      • 1970-01-01
      • 2014-02-15
      • 2017-11-22
      相关资源
      最近更新 更多