Orbusii
Orbusii

Reputation: 270

Core Data: EXC_BAD_ACCESS accessing relationship

I have a crash in a simple Core Data project.

I created a new, empty iPhone project with Core Data. Two entities are added to the data model with a to-one, inverse relationship between them. Both have auto-generated NSManagedObject subclasses. The entity Name and Class are set on both.

DBMove
  attribute: moveValue:Integer16
  relationship: newPosition -> DBPosition (inverse: move)

DBPosition 
  attribute: positionValue:Integer16
  relationship: move -> DBMove (inverse: newPosition)

There is a custom view controller that creates a DBMove and DBPosition. It then sets the inverse relationship between them and saves it. The managed object context is retrieved from the app delegate; I think this is safe because [NSThread isMultiThreaded] returns false.

- (void)newEntries
{
     AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
     NSManagedObjectContext *context = appDelegate.managedObjectContext;

     DBPosition *newPos = [NSEntityDescription
                      insertNewObjectForEntityForName:@"DBPosition"
                      inManagedObjectContext:context];
     newPos.positionValue = [NSNumber numberWithInt:5];

     DBMove *newMove = [NSEntityDescription
                   insertNewObjectForEntityForName:@"DBMove"
                   inManagedObjectContext:context];
     newMove.moveValue = [NSNumber numberWithInt:2];

     newPos.move = newMove;

     NSError *error;
     if (![context save:&error]) {
         NSLog(@"Save error: %@", [error localizedDescription]);
    }
}

The view controller then fetches all the DBMoves. Their Integer16 attributes output as expected. [Edit] Accessing the relationship causes EXC_BAD_ACCESS in main::@autoreleasepool to occur sometime after looping through all the fetch results. Before the crash, all entries of DBMove and DBPosition in the database from previous program runs all print correctly, followed by NSLogs from my ViewController init; the crash appears delayed. It occurs about every 4 out of 5 program runs. [End Edit]

- (void)printMoves
{
     AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
     NSManagedObjectContext *context = appDelegate.managedObjectContext;

     NSError *error;
     NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
     NSEntityDescription *entity = [NSEntityDescription entityForName:@"DBMove"
                                          inManagedObjectContext:context];
    [fetchRequest setEntity:entity];
     NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];

     for (DBMove *move in fetchedObjects) {
         DBPosition *newPos = move.newPosition; // <--- all's well without this reference!
         NSLog(@"Move     : %d", [move.moveValue intValue]);
         NSLog(@"Position : %d", [newPos.positionValue intValue]);
    }
}

NSZombieEnabled=YES causes the program to run without fail and print the expected values. My context appears to be the same throughout the whole execution.

Here is the stack trace:

#0  0x018300b2 in objc_msgSend ()
#1  0x00256ffc in -[_CDSnapshot dealloc] ()
#2  0x0025573d in -[_CDSnapshot release] ()
#3  0x00260384 in -[NSManagedObject(_NSInternalMethods) _clearRawPropertiesWithHint:] ()
#4  0x0026018b in -[NSFaultHandler turnObject:intoFaultWithContext:] ()
#5  0x002607ba in -[NSManagedObject dealloc] ()
#6  0x00257b88 in -[_PFManagedObjectReferenceQueue _processReferenceQueue:] ()
#7  0x00232fe1 in _performRunLoopAction ()
#8  0x01a654ce in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
#9  0x01a6541f in __CFRunLoopDoObservers ()
#10 0x01a43344 in __CFRunLoopRun ()
#11 0x01a42ac3 in CFRunLoopRunSpecific ()
#12 0x01a428db in CFRunLoopRunInMode ()
#13 0x038fe9e2 in GSEventRunModal ()
#14 0x038fe809 in GSEventRun ()
#15 0x0058ed3b in UIApplicationMain ()
#16 0x0000396d in main at /Users/brian/devel/test2-CoreData/test2-CoreData/main.m:16

All the Core Data setup is from the standard inclusion when I checked "Core Data" on the empty new project.

Presumably I'm overreleasing a managed object, but where?

Upvotes: 1

Views: 1735

Answers (1)

Orbusii
Orbusii

Reputation: 270

Beginning the attribute newPosition with "new" was the problem! It was translated into a property with an accessor called "newPosition," which is forbidden in Transitioning to ARC Release Notes:

To allow interoperation with manual retain-release code, ARC imposes a constraint on method naming:

You cannot give an accessor a name that begins with new. This in turn means that you can’t, for example, declare a property whose name begins with new unless you specify a different getter:

// Won't work:
@property NSString *newTitle;

// Works:
@property (getter=theNewTitle) NSString *newTitle;

It is possible to write a custom getter as in the example, but it's much easier to choose a better name and continue using the built-in tool to generate class files.

Note that the compiler will catch this error when using @synthesize but not for @dynamic, as used in auto-generated NSManagedObject classes.

Upvotes: 14

Related Questions