3

I have an EXC_BAD_ACCESS that is killing me. I cannot see where it could be coming from.

ARC code. SymptomRating is a managed object, of course:

__block DPLSymptomRating *rating;
self.editMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];  // retained: strong

NSManagedObjectContext *moc = self.editMOC; //editSymptom.managedObjectContext;

[moc performBlockAndWait:^{
    NSError __autoreleasing *error = nil;
    moc.parentContext = DPLPersonalHxDataStore.shared.managedObjectContext;

    rating = [NSEntityDescription insertNewObjectForEntityForName:@"SymptomRating" inManagedObjectContext:moc];
    [moc assignObject:rating toPersistentStore:[NSPersistentStore MR_defaultPersistentStore]];

    rating.ratingCode = @101;
    rating.symptomCode = 1;
    rating.displayName = @"debug";
    NSAssert(rating!=nil, @"Cannot edit nil rating");
    NSAssert(rating.managedObjectContext==moc, @"");
    NSLog(@"validates for insert: %@", [rating validateForInsert:&error]?@"true":@"false");
    NSAssert(error==nil,@"");
    NSLog(@"inserted objects: %@", moc.insertedObjects);    // 1 object, from above
    NSLog(@"updated objects: %@", moc.updatedObjects);      // empty
    NSLog(@"deleted objects: %@", moc.deletedObjects);      // empty
    [moc save:&error];   // -->FAIL: EXC_BAD_ACCESS (code=2, address=0x2)
    NSLog(@"can we get here?");  // NOPE
    NSAssert(error==nil, @"");
}];

This crashes with the same error whether the concurrency type is Main or Private, and with or without -performBlock: or -performBlockAndWait:

I have NSZombieEnabled (I think -- don't know how to verify if it's actually working).

Backtrace:

frame #0: 0x019c0098 libobjc.A.dylib`objc_msgSend + 12
frame #1: 0x0087e5e0 CoreData`_PFObjectIDFastHash64 + 96
frame #2: 0x01fb48d0 CoreFoundation`__CFDictionaryHashKey + 32
frame #3: 0x01f0a114 CoreFoundation`CFBasicHashFindBucket + 1572
frame #4: 0x01f09ad5 CoreFoundation`CFDictionaryGetValue + 133
frame #5: 0x0088cde8 CoreData`-[NSPersistentStoreCache incrementRefCountForObjectID:] + 40
frame #6: 0x0088cd74 CoreData`-[NSSQLCore managedObjectContextDidRegisterObjectsWithIDs:] + 228
frame #7: 0x009528aa CoreData`-[NSPersistentStoreCoordinator(_NSInternalMethods) _informAffectedStoresOfInterestByChildContextInObjectsWithObjectIDs:withSelector:] + 122
frame #8: 0x0088cc7f CoreData`-[NSPersistentStoreCoordinator(_NSInternalMethods) managedObjectContextDidRegisterObjectsWithIDs:] + 47
frame #9: 0x008bd9e9 CoreData`__-[NSManagedObjectContext(_NestedContextSupport) managedObjectContextDidRegisterObjectsWithIDs:]_block_invoke_1 + 73
frame #10: 0x008bce41 CoreData`internalBlockToNSManagedObjectContextPerform + 17
frame #11: 0x01de5953 libdispatch.dylib`_dispatch_barrier_sync_f_invoke + 61
frame #12: 0x01de5e00 libdispatch.dylib`dispatch_barrier_sync_f + 62
frame #13: 0x008bcde5 CoreData`_perform + 117
frame #14: 0x008bd999 CoreData`-[NSManagedObjectContext(_NestedContextSupport) managedObjectContextDidRegisterObjectsWithIDs:] + 73
frame #15: 0x008bd9e9 CoreData`__-[NSManagedObjectContext(_NestedContextSupport) managedObjectContextDidRegisterObjectsWithIDs:]_block_invoke_1 + 73
frame #16: 0x008bcdc4 CoreData`_perform + 84
frame #17: 0x008bd999 CoreData`-[NSManagedObjectContext(_NestedContextSupport) managedObjectContextDidRegisterObjectsWithIDs:] + 73
frame #18: 0x008ac672 CoreData`-[NSManagedObjectContext(_NSInternalAdditions) _informParentStore:ofInterestInObjects:] + 274
frame #19: 0x008991e8 CoreData`-[NSManagedObjectContext save:] + 536
frame #20: 0x000a06fc MyApp`__53-[DPLNotesViewController editRatingForIndexPath:new:]_block_invoke(.block_descriptor=0xbfffe260) + 1836 at DPLNotesViewController.m:270
frame #21: 0x008bcaf3 CoreData`developerSubmittedBlockToNSManagedObjectContextPerform + 99
frame #22: 0x01de5953 libdispatch.dylib`_dispatch_barrier_sync_f_invoke + 61
frame #23: 0x01de5e00 libdispatch.dylib`dispatch_barrier_sync_f + 62
frame #24: 0x008bca48 CoreData`-[NSManagedObjectContext performBlockAndWait:] + 136
frame #25: 0x0009fe61 MyApp`-[DPLNotesViewController editRatingForIndexPath:new:](self=0x0a367eb0, _cmd=0x0010fb59, indexPath=0x083f9200, new='\x01') + 465 at DPLNotesViewController.m:251
frame #26: 0x0009f786 MyApp`-[DPLNotesViewController tableView:didSelectRowAtIndexPath:](self=0x0a367eb0, _cmd=0x02ba0ff6, tableView=0x08c69a00, indexPath=0x083f9200) + 150 at DPLNotesViewController.m:195
frame #27: 0x00bad71d UIKit`-[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 1164
frame #28: 0x00bad952 UIKit`-[UITableView _userSelectRowAtPendingSelectionIndexPath:] + 201
frame #29: 0x0143586d Foundation`__NSFireDelayedPerform + 389
frame #30: 0x01fc6966 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 22
frame #31: 0x01fc6407 CoreFoundation`__CFRunLoopDoTimer + 551
frame #32: 0x01f297c0 CoreFoundation`__CFRunLoopRun + 1888
frame #33: 0x01f28db4 CoreFoundation`CFRunLoopRunSpecific + 212
frame #34: 0x01f28ccb CoreFoundation`CFRunLoopRunInMode + 123
frame #35: 0x021ac879 GraphicsServices`GSEventRunModal + 207
frame #36: 0x021ac93e GraphicsServices`GSEventRun + 114
frame #37: 0x00b1da9b UIKit`UIApplicationMain + 1175

What else can I try? Help!

Alexej Magura
  • 4,833
  • 3
  • 26
  • 40
c roald
  • 1,984
  • 1
  • 20
  • 30
  • 1
    Have you tried using a "direct" store context (one without a parent context, but only a persistent store)? – Dan Shelly Jul 20 '13 at 16:59
  • Yes, if I simply use the parent context directly, it works fine. – c roald Jul 20 '13 at 18:24
  • 1
    Have you tried removing the `assignObject:toPersistentStore:` call? – Dan Shelly Jul 21 '13 at 04:00
  • Hm. The error goes away if I remove that line. But I had to *add* that line a while ago to prevent a different error. For reasons I couldn't understand, Core Data kept wanting to assign new objects to my read-only store, leading to different errors on save. So I'm reluctant to take that line out, even if it looks like Core Data is doing the right thing now. :( Stepping through in the debugger, MR_defaultPersistentStore is not nil and has the right value. – c roald Jul 21 '13 at 15:40
  • 1
    What is the concurrency type of: `DPLPersonalHxDataStore.shared.managedObjectContext`? have you tried setting the parent context in the parent context thread (make sure the parent is initialized on the appropriate thread) ? – Dan Shelly Jul 22 '13 at 03:28
  • The parent shared context is a Main Queue context. I just tried moving the setParentContext call outside the performBlock, but it didn't help. (I'm triggering this code from UI, so that's the main queue.) I suppose the bad access could be resulting from a notification being delivered to a deallocated object, but I don't know how to track that down. I've got -removeObserver calls for every -addObserver I can find, and I'm not getting anything from NSZombie, and I don't know what else to do to debug that. – c roald Jul 22 '13 at 05:28
  • Holy cow, this may be an iOS 5 bug. I was using that version in the Simulator because it's my lowest targeted OS version, and when I switch to iOS 6.1 the issue goes away -- see http://stackoverflow.com/questions/15028301/core-data-multi-level-parent-child-context#comment22749163_15169791 – c roald Jul 22 '13 at 15:14
  • 1
    If your read only store and your writing store are both using the same configuration, create a unique configuration for each. This is what tells CoreData in which stores to save things. You should NOT be calling assignObject:toPersistentStore: directly. – quellish Jul 22 '13 at 19:46
  • Huh, okay. I've gone back to the docs and found where it says "You typically use configurations if you want to store different entities in different stores", but it doesn't give much more explanation than that. I'm not quite sure I see how that's supposed to help me here -- SymptomRating entities exist in both my read-only and writeable stores, so I think I'd have to include it in both configurations, right? – c roald Jul 22 '13 at 20:13
  • Also, the docs for -assignObject:toPersistentStore: already say "If the receiver’s persistent store coordinator manages only a single writable store, ... object will automatically be assigned to that store". I never figured out why that wasn't working, but explicitly assigning seemed to fix it. If I'm misunderstanding this stuff, I'd love to get straightened out. – c roald Jul 22 '13 at 20:15

3 Answers3

7

The problem turns out to be an iOS 5 bug where parent contexts apparently cannot see their children's new relationships. Forcing Core Data to obtain permanent object IDs before trying to save fixes the problem, like so:

[moc obtainPermanentIDsForObjects:@[rating] error:&error];
[moc save:&error];   // --> WORKS NOW
c roald
  • 1,984
  • 1
  • 20
  • 30
  • I think you got the point with the relationships but this is not a bug in ios5 only, I have seen this in ios6 too and maybe 7. In ios6 i get the error when establishing the relationship and not on save. – João Nunes Sep 13 '13 at 11:02
  • 1
    I have this same issue on iOS 6 (but not iOS 7). There are other reasons to get the crash in `_PFObjectIDFastHash64` which I have seen on iOS 7 (namely crossing the thread boundary with your MOC) but this specific case, as far as I can tell (in my code, where there is no cross-thread MOC use and it crashes on iOS 6 the first time it runs [strangely, with the same set of data in my CD backing store, the second time it does not crash] but not on iOS 7) is "fixed" in iOS 7. I have not, however, been able to alleviate the issue with the `-obtainPermanentIDsForObjects:error` call. Still trying. – chadbag Nov 20 '13 at 22:14
  • I have the same error. I'm getting an EXC_BAD_ACCESS only on iOS 6+, but not in iOS7, when setting a relationship. Have you found any solutions? – amb Mar 31 '14 at 14:39
  • i solved this by forcing a save to the persistent store. So in my case i was changing a child context after asking the permanentID. But the parent context was not yet saved and cause the crash in iOS 6. Try to save both child and parent before changing or accessing the child. One way to check it is to make sure: objectID.isTemporaryID is NO – João Nunes Apr 23 '14 at 11:23
  • I wish I could give you +10 for this – coder Oct 02 '14 at 15:24
1

I can see you are using Magical Record.

In that case you should use their own MR_save commands.

Anyway This is not a iOS 5 only bug but rather iOS 5/6, I cannot reproduce it in 7.

So to solve it as you mention obtaining a permanent ID help. But not only that.

In my case I was changing a child context after asking the permanentID. But the parent context was not yet saved and cause the crash in iOS 6. I tried to save both child and parent before changing or accessing the child and it doesnt crash anymore. One way to check it is to make sure: objectID.isTemporaryID is NO

João Nunes
  • 3,751
  • 31
  • 32
0

Had a similar error (also with using magical record) - the issue turned out to be a combination of creating temporary objects on a thread different than the thread my context was created on, and trying to access the same temporary object references post-save.

So don't do things like:

appContext = [NSManagedObject MR_defaultContext]
temporaryObjects = [thingThatWorksInABackgroundThreadWithContext: appContext]
[appContext saveToPersistentStoreAndWait]
NSLog(@"%@", temporaryObjects[0])

instead do:

[MagicalRecord saveWithBlock:^(NSManagedObjectContext * _Nonnull localContext) {

    NSArray * tempObjects = [thingThatWorksInSameThreadWithContext:localContext]

}
completion:^(BOOL success, NSError *error) {

    NSArray *savedObjects = [NSArray arrayWithArray:[MyManagedObjectType MR_findAll]];
    NSLog(@"%@", savedObjects[0]);

}

You'll also need your platform's specific statements to avoid sudden app termination, see MR's documentation on that and saving here: https://github.com/magicalpanda/MagicalRecord/blob/master/Docs/Saving-Entities.md

Vivek Gani
  • 1,283
  • 14
  • 28