0

I have multiple NSInvocationOperations created and added to an NSOperationQueue. Two of these NSInvocationOperations create lots of objects of the same parent class (Country and City which subclass Location). It has mostly gone well except that I've noticed changes to one model or the other are kinda clobbered.

Looking at the store (using a sqlite program) I see the first City (of maybe 200 total) created and then all of the Countries (again maybe 200) created. If I delete the app and run it again I'll see the first Country and then all of the Cities.

I hit the docs and noticed that Apple suggestions setting up your per thread MOCs in the start method of you NSOperation. However I'm not using an NSOperation, I'm using an NSInvocationOperation. It's actually making me question more so why they suggest creating your MOC in start.

This is my selector for my NSInvocationOperation...

+ (void)load:(NSString *)file
{
    NSManagedObjectContext *managedObjectContext = [(OSSMAppDelegate *)[[UIApplication sharedApplication] delegate] adHocManagedObjectContext];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(mergeChanges:)
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:managedObjectContext];

    SBJsonParser *jsonParser = [[SBJsonParser alloc] init];

    NSString *json = [[NSString alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:file ofType:@"json"]];

    NSArray *objects = [[jsonParser objectWithString:json] valueForKeyPath:@"objects"];

    for(NSDictionary *object in objects)
    {
        [self createObjectWithObject:object inManagedObjectContext:managedObjectContext];
    }

    NSError *error = nil;
    [managedObjectContext save:&error];

}

...from the app delegate...

- (NSManagedObjectContext *)adHocManagedObjectContext
{
    NSManagedObjectContext *adHocManagedObjectContext = nil;

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];

    if (coordinator != nil)
    {
        adHocManagedObjectContext = [[NSManagedObjectContext alloc] init];
        [adHocManagedObjectContext setPersistentStoreCoordinator:coordinator];
        [adHocManagedObjectContext setUndoManager:nil];
    }

    return adHocManagedObjectContext;
}

...then somewhere else (Note: firstRun calls load:)...

NSInvocationOperation *countryInvocationOperation = [[NSInvocationOperation alloc] initWithTarget:[Country class] selector:@selector(firstRun) object:nil];
[operationQueue addOperation:countryInvocationOperation];

Is there any problem with creating the MOC in the selector that's being invoked? I'd image it has to be since the MOC is tied to the thread it's created on. I guess any pointers as to where I'm going wrong is helpful.

rob5408
  • 2,972
  • 2
  • 40
  • 53
  • Is it the same `adHocManagedObjectContext` that is used in all operations? That would be a problem if the operations run in parallel, because a MOC is not thread-safe. – Martin R Apr 17 '13 at 19:09
  • @MartinR Hi, I added that code as well. I create a new MOC with the same persistent store coordinator with each call. – rob5408 Apr 17 '13 at 19:58
  • You MOC setup looks fine to me (it is created per thread). could you please elaborate on the nature of your problem, what do you mean by Class A created and then Class B created? are there any missing items? (don't forget to remove the observer). – Dan Shelly Apr 18 '13 at 03:58
  • @DanShelly I took out the vague Class A/B and put in what I'm actually using (City and Country) and also clarified what I'm seeing in the store. – rob5408 Apr 18 '13 at 14:08

1 Answers1

0

I'm not sure I understand your problem (Do you have missing countries or cities?, do you have incorrect order? give an example of 'clobbered').

As for your question:

Is there any problem with creating the MOC in the selector that's being invoked?

No, there is no problem. the documentation only say it must be created ON the thread you intend to use it (start and main are methods that will run on the operation thread). hence, NSInvocationOperation will run your method in the operation thread, and you can create your MOC there without worries.

Dan Shelly
  • 5,991
  • 2
  • 22
  • 26
  • Sometimes I will see one City created and then 200 Countries. If I delete the app and try again I'll see maybe one Country and 200 Cities. So I know the code I wrote to loop through the JSON and create new objects works. It's just that sometimes after creating a bunch of Cities they don't persist after merging the context. I should be able to create objects of the same (base) type on different threads and have them all be in the store when all is said and done, right? I'll keep running it it and sees if a pattern emerges. – rob5408 Apr 18 '13 at 23:15
  • I don't know any issue with data clobbering when saving a context. I would advise you to print the objects you generate in `createObjectWithObject:inManagedObjectContext:` during creation ,and before the save (the inserted, deleted, updated collections of the context) and verify that all objects you created are there before the save. if before the save you see all your objects in the context and they are still missing from the DB (and no error), then you might have a problem. – Dan Shelly Apr 19 '13 at 06:05
  • Hi Dan, I ran this maybe 50 more times last night and couldn't get what I was seeing the other day. Not sure what has changed. Thanks for your help! – rob5408 Apr 19 '13 at 14:29