核心数据executeFetchRequest抛出NSGenericException(集合在枚举时发生了变异)

时间:2010-07-19 13:40:51

标签: iphone core-data

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

假设有两个托管对象:Department< - >>雇员。同步的工作方式如下:

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

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

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

    *** 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传递了所有托管对象。

    有什么想法吗?

4 个答案:

答案 0 :(得分:14)

我有同样的问题...... 它被解决了,因为我使用的是在后台线程上的主线程上创建的managedObjectContext。 解决方案是在后台线程上创建一个不同的ManagedObjectContext,并使用常规的persistentStoreCoordinator ... 之后它运作良好!

答案 1 :(得分:4)

错误“someCollection在被枚举时被突变”是由于改变了一个可变集合,即数组,字典,集合等,而枚举器正在逐步通过它。由于您无法枚举移动目标,因此会触发错误。

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

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

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

答案 2 :(得分:1)

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

通常,当您在枚举集合的同时修改集合时会发生此类异常。在多线程场景中,它可能还意味着您的集合在没有正确的线程同步的情况下被枚举和同时更新。

答案 3 :(得分:0)

我有同样的问题。你可以使用锁,解锁接收器。直到现在我才解决了这个问题。