#StackBounty: #ios #core-data #nsmanagedobject #nsmanagedobjectcontext Core Data duplicating managed object after changing properties

Bounty: 50

Consider that I have an arbitrary object on a context.

I’m creating a new context with a parent context that has this object.

context2 = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
context2.parentContext = parentContext;

On this new context I do something like this in order to change the object on it’s own context:

NSManagedObjectID *objectID = [managedObject objectID];
NSManagedObject *contextObj = [context2 objectWithID:objectID];

if I do a fetch on the context now, it only displays 1 object, which is expected. But once I do
contextObj.name = @"blah";
the same fetch not returns 2 objects

One has the original name, the other has “blah”;

And the one with the original name has isTemporary on it’s objectID set to YES, but the one with “blah” has it set to NO.

any idea on why the context is making a copy of the object with the old value?

Ex:

NSManagedObjectContext *context2 = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
context2.parentContext = parentContext;

NSManagedObject *contextObj = [context2 objectWithID: myObject.objectID];
//If I do a NSFetchRequest here on the context2, there's only 1 object

contextObj.name = @"test";
//Now, the fetch has 2 objects

Edit:
Here’s a code that replicates the issue:

#import "ViewController.h"
#import "CustomObject+CoreDataClass.h"
#import "SecondCustomObject+CoreDataClass.h"
#import "AppDelegate.h"

@interface ViewController ()

@property (nonatomic, strong) NSManagedObjectContext *secondContext;
@property (nonatomic, strong) SecondCustomObject *secondObject;
@property (nonatomic, strong) NSPersistentContainer *persistentContainer;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 250, 100)];
    [button setTitle:@"Tap here multiple times" forState:UIControlStateNormal];
    [button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
    [button addTarget:self action:@selector(triggerIssue) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];

    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    self.persistentContainer = appDelegate.persistentContainer;

    [self setupConfig];
}

- (void)setupConfig {
    NSManagedObjectContext *firstContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    firstContext.parentContext = self.persistentContainer.viewContext;
    firstContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;

    self.secondContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    self.secondContext.parentContext = firstContext;
    self.secondContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;

    self.secondObject = [[SecondCustomObject alloc] initWithContext:self.secondContext];
    self.secondObject.name = @"Name1";
}

//Needs to be triggered 2-3 times for the issue to happen
- (void)triggerIssue {
    [self.secondContext performBlock:^{
        [self.secondContext save:NULL];
        [self.persistentContainer.viewContext performBlock:^{
            [self.persistentContainer.viewContext save:NULL];
            NSLog(@"SAVED");
        }];
    }];

    [self.secondContext.parentContext refreshAllObjects];
    [self.secondContext.parentContext save:NULL];
    self.secondObject.name = @"Name2";
    [self print];
}

- (void)print {
    NSArray *result = [self.secondContext executeFetchRequest:SecondCustomObject.fetchRequest error:NULL];
    NSLog(@"Result Count [%lu]", (unsigned long)result.count);
    for (SecondCustomObject *obj in result) {
        NSLog(@"Obj Name [%@]", obj.name);
    }
    NSLog(@"-----");
}

After calling triggerIssue some times, you start to see duplicated objects on the same context, here’s an output example:

CDTest[15510:761956] Result Count [1]
CDTest[15510:761956] Obj Name [Name2]
CDTest[15510:761956] -----
CDTest[15510:761956] SAVED
CDTest[15510:761956] Result Count [1]
CDTest[15510:761956] Obj Name [Name2]
CDTest[15510:761956] -----
CDTest[15510:761956] SAVED
CDTest[15510:761956] Result Count [2]
CDTest[15510:761956] Obj Name [Name2]
CDTest[15510:761956] Obj Name [Name2]
CDTest[15510:761956] -----
CDTest[15510:761956] SAVED


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.