I have a background thread persisting JSON data downloaded from a web service in Core Data. This works as expected, except for odd and erratic occasions. I have not been able to identify why this occurs, and can cannot reproduce. This has occurred on both the simulator and on the device, so it is not memory related.
The method that crashes is as follows;
+ (Hotspot *)insertOrUpdateWithDictionary:(NSDictionary *)hotspotJSON error:(NSError **)error {
NSManagedObjectContext *moc = [[ContentProviderController sharedContentProviderController] managedObjectContext];
Hotspot *hotspot = [Hotspot managedObjectWithPrimaryKey:[hotspotJSON objectForKey:@"id"] error:error];
if (*error == nil) {
if (hotspot) {
// Delete hotspot and c开发者_开发问答ascade to other managed objects
[moc deleteObject:hotspot];
}
hotspot = [NSEntityDescription insertNewObjectForEntityForName:@"Hotspot" inManagedObjectContext:moc];
[hotspot setJSONProperties:hotspotJSON error:error];
}
if (*error == nil) {
return hotspot;
}
return nil;
}
managedObjectWithPrimaryKey looks like this;
+ (Hotspot *)managedObjectWithPrimaryKey:(NSString *)primaryKey error:(NSError **)error {
if (primaryKey) {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:NSStringFromClass([self class]) inManagedObjectContext:[[ContentProviderController sharedContentProviderController] managedObjectContext] ]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(hotspotId like %@)", primaryKey];
[fetchRequest setPredicate:predicate];
[fetchRequest setFetchLimit:1];
NSArray *results = [[[ContentProviderController sharedContentProviderController] managedObjectContext] executeFetchRequest:fetchRequest error:error];
RELEASE_SAFELY(fetchRequest);
if (*error == nil && results) {
return ([results count] > 0) ? [results objectAtIndex:0] : nil;
} else {
GTMLoggerError(@"error while retrieving process with hotspotId=%@; %@", primaryKey, [*error description]);
return nil;
}
} else {
*error = [NSError errorWithDomain:kCoreDataPersistenceError code:1000 userInfo:[NSDictionary dictionaryWithObject:@"Attempt to access an Hotspot with an invalid (null) hotspotId." forKey:NSLocalizedDescriptionKey]];
return nil;
}
}
setJSONProperties is simply setting the managed object properties as follow;
- (void)setJSONProperties:(NSDictionary *)dictionary error:(NSError **)error {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//set hotspot properties here
RELEASE_SAFELY(pool);
}
The crash logs looks as follows;
** Call stack at first throw:
(
0 CoreFoundation 0x36b5964f __exceptionPreprocess + 114
1 libobjc.A.dylib 0x33db2c5d objc_exception_throw + 24
2 CoreFoundation 0x36b58edf __NSFastEnumerationMutationHandler + 214
3 libobjc.A.dylib 0x33db936d objc_enumerationMutation + 24
4 CoreData 0x36ee5c89 -[NSManagedObjectContext executeFetchRequest:error:] + 1888
5 magazine 0x000cd76d +[Page managedObjectWithPrimaryKey:error:] + 288
6 magazine 0x000ccc65 -[Hotspot setJSONProperties:error:] + 724
7 magazine 0x000cc943 +[Hotspot insertOrUpdateWithDictionary:error:] + 194
8 magazine 0x000c3d39 -[ContentProviderController persistHotspots:] + 368
9 magazine 0x000c05e5 -[ContentProviderController doPersistenceJobForIssueObjectId:] + 704
10 CoreFoundation 0x36b5c7a4 __invoking___ + 68
11 CoreFoundation 0x36ad443d -[NSInvocation invoke] + 108
12 Foundation 0x34f8043f -[NSInvocationOperation main] + 78
13 Foundation 0x34f19d1b -[__NSOperationInternal start] + 658
14 Foundation 0x34f19a7f -[NSOperation start] + 22
15 Foundation 0x34f7fecb ____startOperations_block_invoke_2 + 46
16 libdispatch.dylib 0x339d88e7 _dispatch_call_block_and_release + 10
17 libdispatch.dylib 0x339d362d _dispatch_worker_thread2 + 252
18 libsystem_c.dylib 0x3483c591 _pthread_wqthread + 264
19 libsystem_c.dylib 0x3483cbc4 _init_cpu_capabilities + 4294967295
)
I fully understand that an object should not be mutated while enumerating, but what I don't understand is why a separate managed object(Page) is seen in this thread(-[Hotspot setJSONProperties:error:] + 724). Each managed object is persisted sequentially, so a "Page" object would have been persisted before this "Hotspot" object.
Okay, so it turns out that the culprit was a very complex combination of events that spawned two simultaneous Core Data threads trying to make changes to the same data.
Stopping this solved the problem.
精彩评论