Thomas Geulen
Thomas Geulen

Reputation: 201

Core Data - update entity in background thread automatically changes NSManagedObject in Main Thread without merging- why?

i´m currently learning Core Data. Core Data is great but i can not explain the behaviour with a second managed object context in a background thread.

I have an entity called TestEntity with 2 attributes (testId and testDescription) On the main thread i fetch the entity with the testId = 1 and store this managed object into an instance variable.

    NSEntityDescription *entityDescription = [NSEntityDescription
                                          entityForName:@"TestEntity" inManagedObjectContext:self.managedObjectContext];

NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];

NSNumber *testId = [NSNumber numberWithInt:1];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
                          @"testId == %@", testId];
[request setPredicate:predicate];

NSError *error = nil;
NSArray *array = [self.managedObjectContext executeFetchRequest:request error:&error];

t1 = [[array objectAtIndex:0] retain];

TestThread *tt = [TestThread new];

NSOperationQueue *queue = [NSOperationQueue new];
[queue addOperation:tt];

[queue waitUntilAllOperationsAreFinished];
NSLog(@"%@", [t1 valueForKey:@"testDescription"]);

Then is start a NSOperation with a NSOperationQueue called TestThread. In the main method of this TestThread i create a second managed object context, fetch the same entity (testId = 1) like the main thread, change the testDescription property and save the new context without any errors.

    tgAppDelegate *delegate = [[NSApplication sharedApplication] delegate];

self.context = [[[NSManagedObjectContext alloc] init] autorelease];
[context setPersistentStoreCoordinator:delegate.persistentStoreCoordinator];


NSEntityDescription *entityDescription = [NSEntityDescription
                                          entityForName:@"TestEntity" inManagedObjectContext:self.context];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];

// Set example predicate and sort orderings...
NSNumber *testId = [NSNumber numberWithInt:1];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
                          @"testId == %@", testId];
[request setPredicate:predicate];

NSError *error = nil;

NSArray *array = [context executeFetchRequest:request error:&error];
TestEntity *t1 = [array objectAtIndex:0];

t1.testDescription = @"abc";
[context save:&error];
if (error) {
    // do something
}

The behaviour i can not explain my self is, that the NSLog output after

[queue waitUntilAllOperationsAreFinished];

has the same string value which updated in my background thread.

abc

My understanding of Core Data and Multithreading is that i have to do an explicit merge between contextes. In my test app there is no merge.

Can anyone explain why this happenes?

Upvotes: 3

Views: 1668

Answers (1)

Lukas
Lukas

Reputation: 1543

By default, a fetch request will return objects as faults so their content is not actually loaded until you access it (you can learn more about faulting under this link).

To see your expected behavior, try logging the original value of t1.testDescription before you start the second thread.

You can also set

self.context.returnObjectsAsFaults = NO;

but this might have a negative impact on the memory footprint of your app.

Upvotes: 1

Related Questions