8

I have been trying to figure out this problem for 2 days now. I keep getting an error when I try to save.

//self.data is NSManagedObject. kAppDelegate.moc is the managed object context.
self.data = [NSEntityDescription insertNewObjectForEntityForName:@"Data"
                                 inManagedObjectContext:kAppDelegate.moc];

[self.data setValue:[NSNumber numberWithBool:NO] forKey:@"isit"];
[self.data setValue:@"" forKey:@"name"];

NSError *error;
if(![self.data.managedObjectContext save:&error])
{
    NSLog(@"Save did not complete successfully. Error: %@",
    [error localizedDescription]);
}

When I run it though, this appears in the console:

"CoreData: error: Mutating a managed object 0x10935d4c0 (0x10935d420) after it has been removed from its context."

And this:

Save did not complete successfully. Error: (null)

I can't figure out why this is happening, or why the error is "null".

JOM
  • 8,139
  • 6
  • 78
  • 111
user3251270
  • 107
  • 1
  • 8
  • 1
    You removed an object from its context and tried to change it. You can't change an orphaned object. Fix that first and then we can sort out the save. – duci9y Jul 25 '14 at 22:34
  • i'm sorry if this is a dumb question but I can't detect where I did that. Do you see it in this code? – user3251270 Jul 25 '14 at 22:46
  • Well… I'd suggest searching your project for calls to `deleteObject:`. – duci9y Jul 25 '14 at 22:51
  • There is one message like that in the project but it doesn't get called at the time when the program tells me i'm mutating objects that are removed. – user3251270 Jul 25 '14 at 22:55
  • Pause the debugger when core data throws that error and `po` the offending object. – duci9y Jul 25 '14 at 22:56
  • The offending object seems to be self.data in the code snippet above, but I don't see how it can be removed when it was just added to the context. What do you mean by po? – user3251270 Jul 25 '14 at 23:04
  • The lldb command, `po`. Pause your program when the error occurs, type `po 0x123456789` in the debugger console, replacing `0x123456789` with the address of the object core data tells you the error occurred at. – duci9y Jul 25 '14 at 23:07
  • yep it still says it's data – user3251270 Jul 25 '14 at 23:17
  • 4
    Thanks for your help but i figured it out. My managedObjectContext was being deleted every time i accessed it, and then getting replaced. Hard to explain but I just coded it wrong – user3251270 Jul 26 '14 at 13:42
  • 3
    Please add your own answer if you think it will help others or delete the question ;-) – Wain Jul 26 '14 at 22:23

3 Answers3

3

Given this error:

2015-07-06 06:15:05.124 xxx[3609:796500] CoreData: error: Mutating a managed object 0x17423d3e0 (0x1740d0450) after it has been removed from its context.

Found:

In my case; a trace of the initialization sequence (using breakpoints and log message class_initial:%B:%H) revealed that I was creating the context twice. My solution was to simply redirect the redundant call to self.managedObjectContext. I may take time at a later point to track down and eliminate the redundant logic.

Initial Results:

  1. d: init(modelName:):1
  2. mext: findInStore(_:):1
  3. mext: findInStore(_:sortDescriptors:predicate:):1
  4. mext: NSManagedObject:1
  5. d:context DataStore:1
  6. d:persistentStoreCoordinator :1
  7. d: managedObjectModel:1
  8. d: applicationDocumentsDirectory:1
  9. mext: createInStore(_:):1
  10. mext: NSManagedObject:2
  11. d:context DataStore:2

Final Results

  1. db: init(modelName:databaseName:):1
  2. d: init(modelName:):1
  3. mext: findInStore(_:):1
  4. mext: findInStore(_:sortDescriptors:predicate:):1
  5. mext: NSManagedObject:1
  6. d:managedObjectContext managedObjectContext:1
  7. d:persistentStoreCoordinator :1
  8. d: managedObjectModel:1
  9. d: applicationDocumentsDirectory:1

Recommendation:

For others having this problem, I recommend a close inspection of your Core Data Stack's initialization sequence. A context may be created twice or the managed object might be getting deleted.

Tommie C.
  • 12,895
  • 5
  • 82
  • 100
3

There is another way to provoke the "CoreData: error: Mutating a managed object ... after it has been removed from its context." message.

  • If you have a multiple layer core data stack
  • You hold a strong reference to a managed object in the foreground
  • A background thread updates that same core data object

When the update bubbles up to the foreground thread it invalidates your object. You need to detect that and retrieve the new version.

Hold a weak reference in a cache object. Then write a getter that checks that cache object for nil and retrieves the new version of the object.

weak var cachedObject: NSManagedObject?

var object: NSManagedObject {

    get {

        objc_sync_enter( self )
        defer {

            objc_sync_exit( self)
        }

        guard nil == cachedObject else {

            return cachedObject!
        }

        guard let object = // **** retrieve object here ****

            fatalError( "managed object does not exist" )
        }

        cachedObject = object

        return cachedObject!
    }

    set {

        cachedObject = newValue
    }
}

Or, you could just retrieve the object every time in the getter.

Bob Wakefield
  • 814
  • 9
  • 16
  • Thanks. I was using a scratch context, and changing the variable holding the object to `weak`, fixed this warning and now it saves correctly. – Jason Moore Oct 29 '19 at 16:26
1

This error can also result when your moc is mistakenly a weak reference and is garbage collected before you get a chance to use it. Don't ask me how I know this...

AdvApp
  • 1,094
  • 1
  • 14
  • 27