I wrote a minimal code example of the issue I am having. I implemented the background work two ways: manually spawning threads and letting NSOperation
handle threading. In both cases I am creating NSManagedObjectContexts
for each thread/operation.
When I spawn the threads myself with performSelectorInBackground:withObject:
everything works fine. When I switch to passing my objects off to an NSOperationQueue
I see the following errors when attempting to save the operations NSManagedObjectContext
.
-EXC_BAD_ACCESS - Serious application error. Exception was caught during Core Data change processing: *** -[NSCFSet addObject:]: attempt to insert nil with userInfo (null) - _referenceData64 only defined for abstract class. Define -[NSTemporaryObjectID_default _referenceData64]!
I believe the bug, especially given the last error, has to do with using a temporary objectID to pass objects between threads/contexts. Possibly, even worse, I'm somehow passing NSManagedObjects
between threads.
Either way, I can't find any code which would suggest I am doing so.
My minimal code example can be found here.
Most of the work is done in the AppDelegate
in awakeFromNib
. Set EXECUTE_WITH_NSOPERATION
to 0 to run with performSelectorInBackground:withObject:
. Leave EXECUTE_WITH_NSOPERATION
on 1 to execute with the NSOperationQueue
which creates a bunch of MCBoardParse
objects.
I'm only seeing this under 10.6.
Original
I have a Cocoa application built on 10.5 frameworks. In an NSOperation
In a loop I am quickly creating hundreds of NSManagedObjects
. Frequently the creation of those NSManagedObejcts
will crash with a EXC_BAD_ACCESS error. This happens under both reference counted memory management and garbage collection.
for (offsetCount; offsetCount < [parsedData count]; offsetCount++) {
NSManagedObject *child = [NSEntityDescription insertNewObjectForEntityForName:@"Thread" inManag开发者_Python百科edObjectContext:[self moc]];
Thumbnail *thumb = [Thumbnail insertInManagedObjectContext:[self moc]];
Image *image = [Image insertInManagedObjectContext:[self moc]];
...
}
Thumbnail and Image are both subclasses of NSManagedObject
generated with mogenerator. insertInManagedObjectContext:
looks like
NSParameterAssert(moc_);
return [NSEntityDescription insertNewObjectForEntityForName:@"Thumbnail" inManagedObjectContext:moc_];
NSParameterAssert(moc_);
return [NSEntityDescription insertNewObjectForEntityForName:@"Image" inManagedObjectContext:moc_];
The NSManagedObjectContext returned by [self moc] is created for the NSOperation with
NSPersistentStoreCoordinator *coord = [(MyApp_AppDelegate *)[[NSApplication sharedApplication] delegate] persistentStoreCoordinator];
self.moc = [[NSManagedObjectContext alloc] init];
[self.moc setPersistentStoreCoordinator:coord];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(contextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:self.moc];
[self.moc setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
[self.moc setUndoManager:nil];
[self.moc setRetainsRegisteredObjects:YES];
moc is defined as (nonatomic, retain)
and synthesized. As far as I can tell it, the persistent store and my appDelegate
have no reason to be and are not being garbage collected.
The stack trace looks like
Thread 2 Crashed: Dispatch queue: com.apple.root.default-priority
0 libauto.dylib 0x00007fff82d63600 auto_zone_root_write_barrier + 688
1 libobjc.A.dylib 0x00007fff826f963b objc_assign_strongCast_gc + 59
2 com.apple.CoreFoundation 0x00007fff88677068 __CFBasicHashAddValue + 504
3 com.apple.CoreFoundation 0x00007fff88676d2f CFBasicHashAddValue + 191
4 com.apple.CoreData 0x00007fff82bdee5e -[NSManagedObjectContext(_NSInternalAdditions) _insertObjectWithGlobalID:globalID:] + 190
5 com.apple.CoreData 0x00007fff82bded24 -[NSManagedObjectContext insertObject:] + 148
6 com.apple.CoreData 0x00007fff82bbd75c -[NSManagedObject initWithEntity:insertIntoManagedObjectContext:] + 716
7 com.apple.CoreData 0x00007fff82bdf075 +[NSEntityDescription insertNewObjectForEntityForName:inManagedObjectContext:] + 101
8 com.yourcompany.MyApp 0x000000010002c7a7 +[_Thumbnail insertInManagedObjectContext:] + 256 (_Thumbnail.m:14)
9 com.yourcompany.MyApp 0x000000010002672d -[ThreadParse main] + 10345 (ThreadParse.m:174)
10 com.apple.Foundation 0x00007fff85ee807e -[__NSOperationInternal start] + 698
11 com.apple.Foundation 0x00007fff85ee7d23 ____startOperations_block_invoke_2 + 99
12 libSystem.B.dylib 0x00007fff812bece8 _dispatch_call_block_and_release + 15
13 libSystem.B.dylib 0x00007fff8129d279 _dispatch_worker_thread2 + 231
14 libSystem.B.dylib 0x00007fff8129cbb8 _pthread_wqthread + 353
15 libSystem.B.dylib 0x00007fff8129ca55 start_wqthread + 13
My app is crashing in other places with EXC_BAD_ACCESS but this is code that it happens most with. All of the stack traces look similar and have something to do with CFHash
.
If you are crashing with a exc_bad_access that means you are over-releasing an object or you are calling methods on an object after it has been released. Both of these situations are bad and have nothing to do with Core Data. The fact that you are using garbage collection is probably a clue that something is getting dereferenced and therefore getting garbage collected before you expect it to.
First question though, are you creating a new NSManagedObjectContext
for each one of these NSOperation instances?
Second, I would recommend turning on NSZombie
(which I believe you can do through instruments now) and that will help narrow down what code is calling an object after release, etc. Doing a Google search on NSZombie
and Instruments will turn up several how-to articles.
update
Since this is a 10.6 issue only then it may have to do with the NSOperation instances instead of the Core Data instances. Are your operations flagged as concurrent? The reason I ask is that NSOperation ignores the concurrent flag on 10.6 and that can lead to some nasty surprises.
update 2
Just a note while I am reviewing the overall problem. The line:
self.moc = [[NSManagedObjectContext alloc] init];
Will leak memory (at least while GC is off) if you do not have a release as the alloc init will increment the retain count and then the [self setMoc:] call also increments the retain count.
精彩评论