核心数据崩溃:集合在枚举时发生了变异
Core Data crash: Collection was mutated while being enumerated
当我尝试执行提取时,我的应用程序似乎崩溃了。我正在使用魔法记录。错误信息是:
Collection <__NSCFSet: 0x17005a1f0> was mutated while being enumerated.
对我来说,这表明我们在执行提取时正在更改上下文中的对象,但我是新手,所以我可能是错的。
这是它指向的代码:
- (void) buildAndFetchFRCsInContext:(NSManagedObjectContext*)context{
[context performBlock:^{
__unused NSDate* start = [NSDate date];
self.contactsFRC = [self buildFetchResultsControllerForClass:[Contact class] sortedBy:@"id" withPredicate:nil inContext:context];
self.callsFRC = [self buildFetchResultsControllerForClass:[Call class] sortedBy:@"id" withPredicate:nil inContext:context];
self.newsItemsFRC = [self buildFetchResultsControllerForClass:[NewsItem class] sortedBy:@"id" withPredicate:nil inContext:context];
NSError* error;
// Peform the fetches
[self.contactsFRC performFetch:&error];
[self.callsFRC performFetch:&error];
[self.newsItemsFRC performFetch:&error]; //Crash points to this line
NSLog(@"Spent [%@s] performing fetchs for counts!", @(fabs([start timeIntervalSinceNow])));
[self calculateAndBroadcastCounts];
}];
}
传入的上下文是:
- (instancetype) initWithUserSession:(BPDUserSession*)userSession{
self = [super init];
...
self.context = [NSManagedObjectContext MR_context];
[self buildAndFetchFRCsInContext:self.context];
...
}
我认为这个class是在主线程中初始化的,但是performBlock将块添加到队列中,然后从不同的线程执行。但我不认为这是真的,因为 performBlock 的目的是在另一个线程上执行该块。
根据我发布的内容,谁能告诉我问题出在哪里?
更新:
我尝试将 buildFetchResultsController
调用移动到执行块之外:
- (void) buildAndFetchFRCsInContext:(NSManagedObjectContext*)context{
self.contactsFRC = [self buildFetchResultsControllerForClass:[Contact class] sortedBy:@"id" withPredicate:nil inContext:context];
self.callsFRC = [self buildFetchResultsControllerForClass:[Call class] sortedBy:@"id" withPredicate:nil inContext:context];
self.newsItemsFRC = [self buildFetchResultsControllerForClass:[NewsItem class] sortedBy:@"id" withPredicate:nil inContext:context];
NSMutableArray *list = [[NSMutableArray alloc] initWithCapacity:100];
for (int i = 0; i < 100; i++) {
list[i] = [self buildFetchResultsControllerForClass:[NewsItem class] sortedBy:@"id" withPredicate:nil inContext:context];
}
[context performBlock:^{
__unused NSDate* start = [NSDate date];
NSError* error;
// Peform the fetches
[self.contactsFRC performFetch:&error];
[self.callsFRC performFetch:&error];
[self.newsItemsFRC performFetch:&error];
for (int i = 0; i < list.count; i++) {
[list[i] performFetch:&error]; // Generally error is thrown on i = 5 ~> 10
}
NSLog(@"Spent [%@s] performing fetchs for counts!", @(fabs([start timeIntervalSinceNow])));
[self calculateAndBroadcastCounts];
}];
}
但这仍然失败。我能够用上面显示的循环重现失败。我还尝试使用 NSPrivateQueueConcurrencyType
创建一个新的上下文以在实际的 performBlock 闭包中使用,但这也没有用,同样的问题。
注意:我使用的是 MagicalRecords,所以对于那些不熟悉的人来说,[NSManagedObjectContext MR_context];
等同于返回的上下文:
NSManagedObjectContext *context = [[self alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; // Here self is NSManagedObjectContext
[context setParentContext:parentContext];
[context MR_obtainPermanentIDsBeforeSaving];
return context;
我的问题是我正在对上下文执行保存并且该保存存在并发问题,我试图从非主线程保存在主线程上创建的对象。将该代码移至主线程修复了崩溃。
当我尝试执行提取时,我的应用程序似乎崩溃了。我正在使用魔法记录。错误信息是:
Collection <__NSCFSet: 0x17005a1f0> was mutated while being enumerated.
对我来说,这表明我们在执行提取时正在更改上下文中的对象,但我是新手,所以我可能是错的。
这是它指向的代码:
- (void) buildAndFetchFRCsInContext:(NSManagedObjectContext*)context{
[context performBlock:^{
__unused NSDate* start = [NSDate date];
self.contactsFRC = [self buildFetchResultsControllerForClass:[Contact class] sortedBy:@"id" withPredicate:nil inContext:context];
self.callsFRC = [self buildFetchResultsControllerForClass:[Call class] sortedBy:@"id" withPredicate:nil inContext:context];
self.newsItemsFRC = [self buildFetchResultsControllerForClass:[NewsItem class] sortedBy:@"id" withPredicate:nil inContext:context];
NSError* error;
// Peform the fetches
[self.contactsFRC performFetch:&error];
[self.callsFRC performFetch:&error];
[self.newsItemsFRC performFetch:&error]; //Crash points to this line
NSLog(@"Spent [%@s] performing fetchs for counts!", @(fabs([start timeIntervalSinceNow])));
[self calculateAndBroadcastCounts];
}];
}
传入的上下文是:
- (instancetype) initWithUserSession:(BPDUserSession*)userSession{
self = [super init];
...
self.context = [NSManagedObjectContext MR_context];
[self buildAndFetchFRCsInContext:self.context];
...
}
我认为这个class是在主线程中初始化的,但是performBlock将块添加到队列中,然后从不同的线程执行。但我不认为这是真的,因为 performBlock 的目的是在另一个线程上执行该块。
根据我发布的内容,谁能告诉我问题出在哪里?
更新:
我尝试将 buildFetchResultsController
调用移动到执行块之外:
- (void) buildAndFetchFRCsInContext:(NSManagedObjectContext*)context{
self.contactsFRC = [self buildFetchResultsControllerForClass:[Contact class] sortedBy:@"id" withPredicate:nil inContext:context];
self.callsFRC = [self buildFetchResultsControllerForClass:[Call class] sortedBy:@"id" withPredicate:nil inContext:context];
self.newsItemsFRC = [self buildFetchResultsControllerForClass:[NewsItem class] sortedBy:@"id" withPredicate:nil inContext:context];
NSMutableArray *list = [[NSMutableArray alloc] initWithCapacity:100];
for (int i = 0; i < 100; i++) {
list[i] = [self buildFetchResultsControllerForClass:[NewsItem class] sortedBy:@"id" withPredicate:nil inContext:context];
}
[context performBlock:^{
__unused NSDate* start = [NSDate date];
NSError* error;
// Peform the fetches
[self.contactsFRC performFetch:&error];
[self.callsFRC performFetch:&error];
[self.newsItemsFRC performFetch:&error];
for (int i = 0; i < list.count; i++) {
[list[i] performFetch:&error]; // Generally error is thrown on i = 5 ~> 10
}
NSLog(@"Spent [%@s] performing fetchs for counts!", @(fabs([start timeIntervalSinceNow])));
[self calculateAndBroadcastCounts];
}];
}
但这仍然失败。我能够用上面显示的循环重现失败。我还尝试使用 NSPrivateQueueConcurrencyType
创建一个新的上下文以在实际的 performBlock 闭包中使用,但这也没有用,同样的问题。
注意:我使用的是 MagicalRecords,所以对于那些不熟悉的人来说,[NSManagedObjectContext MR_context];
等同于返回的上下文:
NSManagedObjectContext *context = [[self alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; // Here self is NSManagedObjectContext
[context setParentContext:parentContext];
[context MR_obtainPermanentIDsBeforeSaving];
return context;
我的问题是我正在对上下文执行保存并且该保存存在并发问题,我试图从非主线程保存在主线程上创建的对象。将该代码移至主线程修复了崩溃。