Best approach to update managed objects from async web services responses?

后端 未结 1 1543
粉色の甜心
粉色の甜心 2021-01-16 14:11

I have an NSManagedObjectContext associated to main thread (mainContext), where I fetch all the NSManagedObject I show throughout the

相关标签:
1条回答
  • 2021-01-16 15:00

    I'm not sure this is a full answer for you but I'm working on a somewhat similar situation. The implementation path I've taken is to use child objects (all over the place) - or more like temporary child context's as needed.

    The first thing I should mention, however, is make sure to use the CoreData debug functionality build into XCOde. I made a second Run-Scheme that has

    -com.apple.CoreData.ConcurrencyDebug 1

    On application init I have my normal NSManagedObjectContext - which is passed around to my background networking thread.

    NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
       [child setParentContext:parentObjectContext];
    

    Then whenever I need to pass something from parent to child I end up doing:

    [child objectWithID:(object-in-parent-context)]
    

    or I end up doing

    __block AHRS_RPYL * ret;
    
    [[self getChildContext] performBlockAndWait:^{
    
        ret = [NSEntityDescription insertNewObjectForEntityForName:@"RPYL" inManagedObjectContext:[self getChildContext]];
    
    // ... lots of code
    }];
    

    I can't say I really "love" this approach and I'm sort of stuck with it for the moment however it does seem to work well enough.

    Between most of my view controllers I have a

    @synthesize managedObjectContext;
    

    And in my prepareForSegue method

    // Pass on managedObjectContext
    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    
        //    dispatch_async(dispatch_get_main_queue(), ^{
        // If the destination VC is able to take teh setManagedObjectContext method the current objectContext will be passed along.
        if ([segue.destinationViewController respondsToSelector:@selector(setManagedObjectContext:)]) {
            [segue.destinationViewController performSelector:@selector(setManagedObjectContext:)
                                                  withObject:self.managedObjectContext];
    
        } else {
    
            NSLog(@"Segue to controller [%@] that does not support passing managedObjectContext", [segue destinationViewController]);
        }
        //    });
    }
    

    Summary

    • I've worked with your second choice option and it is doable although it feels kind of clunky in my honest opinion. The truth is we are looking into switching to Realm instead of CoreData because no matter how you look at it CoreData is not the most threading friendly option.

    UDPATES

    Save Code

    I actually save to the child context every 50 messages and to the parent every 250 it looks like (haven't touched this code in ages). I can't promise this is the best correct way to do things but it does work and it does keep disc access to a minimum. I am getting A LOT of messages like 20+ a second so i wanted to do this stack type. You may not care

    (self.getChild()) returns the child context - its older code i left here.

    if (msgCount % 50 == 0) {
                    // Child Save!
    
                    //                    NSLog(@"Saving SDatas");
                    __block NSManagedObjectContext *currentChild = [self getChildContext];
                    [self incChildContext];
    
                    // Parent-Child save methodology
                    [currentChild performBlock:^{
                        NSError *error;
                        if (![currentChild save:&error]) {
                            NSLog(@"\n error => %@ \n", [error localizedDescription]);
                            NSLog(@" error => %@ ", [error userInfo]);
                            [NSException raise:@"Database Write Error" format:@"%@ %@", [error localizedDescription], [error userInfo]];
    
                            //                           abort();
                        }
    
                        if (msgCount % 250 < 5) {
    
                            [parentObjectContext performBlock:^{
                                NSError *error;
                                if (![parentObjectContext save:&error]) {
                                    NSLog(@"\n error => %@ \n", [error localizedDescription]);
                                    NSLog(@" error => %@ ", [error userInfo]);
                                    [NSException raise:@"Database Write Error" format:@"%@ %@", [error localizedDescription], [error userInfo]];
    
                                    //                                   abort();
    
    
                                }
                            }];
                        }
    
                        [currentChild reset];
                    }];
    
    
                }
    

    Deleting

     [childContext performBlock:^{
    
     // LOTS OF CODE
     // to build a query set of the records we want to kill
    // End lots of code
    [childContext deleteObject:msg];
    
    
            if (i % modFactor == 0) {
                self.percentDone = i / totalRecords;
    
                NSLog(@"%.1f Saving ...", self.percentDone * 100);
    
    
                NSError *error;
                if (![childContext save:&error]) {
                    NSLog(@"\n error => %@ \n", [error localizedDescription]);
                    NSLog(@" error => %@ ", [error userInfo]);
                    [NSException raise:@"Database Write Error" format:@"%@ %@", [error localizedDescription], [error userInfo]];
    
                    //                    abort();
                }
    
                [parentContext performBlock:^{
                    NSError *errrror;
                    if (![parentContext save:&errrror]) {
                        NSLog(@"\n error => %@ \n", [error localizedDescription]);
                        NSLog(@" error => %@ ", [error userInfo]);
                        [NSException raise:@"Database Write Error" format:@"%@ %@", [error localizedDescription], [error userInfo]];
    
                        //                        abort();
                    }
                }];
            }
    
    }];  
    

    ...

    Again - like i said before this may not be the best way of doing things but i do have a parent/child stack and it does work. This was one of the first iOS apps I wrote and to do it over again i'd punt on core data and use something else.

    We had an extremely high update rate so when we were using a single stack it was making the UI very slow. The migration to parent/child was slow - were i to do it again I think it would have gone smoother because i'd write many helper functions to deal with some of this (or just use swift).

    good luck.

    0 讨论(0)
提交回复
热议问题