0

in my app I am using Core Data with store type NSSQLiteStoreType. Starting ios 8 sdk all relations of object not fetching from Core Data. Can anyone explain what is the difference with Core Data in ios 8 and how can I fix this issue?

Here is a code of my DataService:

-(id)init{
    self = [super init];
    if (self){
        coreDataHelpersForDispatchQueues = [NSMutableDictionary dictionary];
        contextsForQispatchQueues = [NSMutableDictionary dictionary];

        _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"Name" withExtension:@"momd"]];

        _persistentStoreCoordinator = [self createStoreCoordinatorWithDataModel:_managedObjectModel storeUrl:[NSURL fileURLWithPath:[self databaseLocationPath:@"Name.sqlite"]]];

        self.coreDataHelper = self.coreDataHelperForCurrentDispatchQueue;
        _managedObjectContext = self.coreDataHelper.moc;
        _managedObjectContext.mergePolicy = NSOverwriteMergePolicy;
        [self logCount];

    }
    return self;
}

-(void)logCount{
    @synchronized(coreDataHelpersForDispatchQueues){
        //NSLog(@"count|%d|",coreDataHelpersForDispatchQueues.count);
    }
    [self performSelector:@selector(logCount) withObject:nil afterDelay:1];
}


#pragma mark - currentCDH

-(CoreDataHelper*)coreDataHelperForCurrentDispatchQueue{
    @synchronized(coreDataHelpersForDispatchQueues){
        CoreDataHelper* helper = coreDataHelpersForDispatchQueues[dispatch_current_queue_id];
        if (!helper){
            helper = [[CoreDataHelper alloc] initWithContext:self.newManagedObjectContext];
            helper.retainCounter = 1;
            coreDataHelpersForDispatchQueues[dispatch_current_queue_id] = helper;
            NSLog(@"new helper|%@| context|%@| queue|%s|",helper,helper.moc,dispatch_queue_get_label(dispatch_get_current_queue()));
        }
        return helper;
    }
}

-(void)retainCoreDataHelper:(CoreDataHelper*)coreDataHelper_{
    coreDataHelper_.retainCounter ++;
}

-(void)releaseCoreDataHelper:(CoreDataHelper *)coreDataHelper_ now:(BOOL)now_{
    if (!coreDataHelper_ || (coreDataHelper_.moc == self.coreDataHelper.moc && !now_))
        return;

    coreDataHelper_.retainCounter --;
    if (now_ || coreDataHelper_.retainCounter <= 0){
        __block CoreDataHelper* helper = coreDataHelper_;

        dispatch_block_t doit = ^{
            NSLog(@"consuming helper|%@| context|%@| started",helper,helper.moc);
            [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:helper.moc];

            @synchronized(contextsForQispatchQueues){
                NSNumber* keyToRemove = nil;
                for (NSNumber* key in contextsForQispatchQueues) {
                    if (contextsForQispatchQueues[key] == helper.moc){
                        keyToRemove = key;
                        break;
                    }
                }
                if (keyToRemove)
                    [contextsForQispatchQueues removeObjectForKey:keyToRemove];
            }

            @synchronized(coreDataHelpersForDispatchQueues){
                NSNumber* keyToRemove = nil;
                for (NSNumber* key in coreDataHelpersForDispatchQueues) {
                    if (coreDataHelpersForDispatchQueues[key] == coreDataHelper_){
                        keyToRemove = key;
                        break;
                    }
                }
                if (keyToRemove)
                    [coreDataHelpersForDispatchQueues removeObjectForKey:keyToRemove];
            }
            NSLog(@"consuming helper|%@| context|%@| finished",helper,helper.moc);
        };

        if (now_){
            NSLog(@"will consume helper|%@| context|%@| queue|%s| just now",helper,helper.moc,dispatch_queue_get_label(dispatch_get_current_queue()));
            doit();
        }else{
            int64_t delayInSeconds = 5.0;
            NSLog(@"will consume helper|%@| context|%@| queue|%s| in %lld seconds",helper,helper.moc,dispatch_queue_get_label(dispatch_get_current_queue()),delayInSeconds);
            dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
            dispatch_after(popTime, dispatch_get_global_queue(0, 0), doit);
        }
    }
}

-(NSManagedObjectContext*)newManagedObjectContext{
    NSManagedObjectContext* context = [[NSManagedObjectContext alloc] init];
    context.persistentStoreCoordinator = self.persistentStoreCoordinator;
    @synchronized(contextsForQispatchQueues){
        contextsForQispatchQueues[dispatch_current_queue_id] = context;
    }
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleContextDidSaveNotification:) name: NSManagedObjectContextDidSaveNotification object:context];
    return context;
}

-(void)handleContextDidSaveNotification:(NSNotification *)notification {
    @synchronized(contextsForQispatchQueues){
        NSManagedObjectContext* savedContext = notification.object;
        for (NSManagedObjectContext* context in contextsForQispatchQueues.allValues) {
            if (context != savedContext)
                [context mergeChangesFromContextDidSaveNotification:notification];
        }
    }
}

#pragma mark -

-(NSPersistentStoreCoordinator*)createStoreCoordinatorWithDataModel:(NSManagedObjectModel*)model storeUrl:(NSURL*)url{
    NSError *error = nil;
    NSPersistentStoreCoordinator* cordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];

    NSDictionary *options = @{
                              NSSQLitePragmasOption: @{@"journal_mode": @"OFF"}
                              };

    if (![cordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:options error:&error]){
        [[NSFileManager defaultManager] removeItemAtURL:url error:nil];
        NSLog(@"database was purged: %@",url.absoluteString);
        NSError* error2 = nil;
        if (![cordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:options error:&error2]) {
            NSLog(@"Unresolved error %@, %@", error2, [error2 userInfo]);
            abort();
        }
    }
    return cordinator;
}

-(NSManagedObjectContext*)createManagedObjectContextForStoreCoordinator:(NSPersistentStoreCoordinator*)coordinator{
    NSManagedObjectContext* context = [[NSManagedObjectContext alloc] init];
    context.persistentStoreCoordinator = coordinator;

//    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleContextSavedNotification:) name:NSManagedObjectContextDidSaveNotification object:context];
    return context;
}

#pragma mark - Core Data stack


-(NSString*)databaseLocationPath:(NSString*)databaseName{

    NSArray* documentDirectoryFolderLocation = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    return [[documentDirectoryFolderLocation objectAtIndex:0] stringByAppendingPathComponent:databaseName];
}

Update:

Here is a code how I fetch results:

 NSFetchRequest* oldInstructionsRequest = [[NSFetchRequest alloc] initWithEntityName:@"Instruction"];
                    oldInstructionsRequest.predicate = [NSPredicate predicateWithFormat:@"isFromList == NO && isForUpload == NO"];
                    oldInstructions = [app.dataService.managedObjectContextCopy executeFetchRequest:oldInstructionsRequest error:nil];

<Instruction: 0x190fae40> (entity: Instruction; id: 0x190fa7b0 <x-coredata://BDC5225D-BD51-412B-A247-C0A66947EA74/Instruction/p1> ; data: {
    author = Public;
    color = "";
    created = "2014-02-28 10:21:50 +0000";
    estimation = 0;
    evaluation = nil;
    favourite = 0;
    id = "100b1974-0cdd-4d94-a34f-caf3e2c0cc08";
    imageId = 1955;
    isForUpdate = 0;
    isForUpload = 0;
    isFromList = 1;
    isNew = 0;
    isRemoved = 0;
    modified = "2014-04-18 16:33:55 +0000";
    name = "Types of steps";
    state = 4;
    steps = "<relationship fault: 0x17f45ab0 'steps'>";
}),
revolutionkpi
  • 2,572
  • 10
  • 38
  • 82

1 Answers1

1

Your approach to Core Data concurrency (using dispatch queues) it's not guaranteed to work. You can access objects from a NSManagedObjectContext only from a single thread, but a block scheduled on a dispatch queue can be executed on different threads.

Use performBlock: or performBlockAndWait: methods from NSManagedObjectContext. You won't need @synchronized statements at all if you do this.

Michał Ciuba
  • 7,611
  • 2
  • 29
  • 56
  • Thanks. What is your suggestion, how to fix my code above? – revolutionkpi Jan 15 '15 at 09:52
  • The code has many issues. Get rid of `CoreDataHelper` at all, it's an overkill which probably has been developed before new concurrency APIs were introduced to Core Data in iOS 5. Common approach is to create two managed object contexts: one for main thread and second for the background thread, which saves data to the persistent store. See this answer: http://stackoverflow.com/a/24663533/2128900 – Michał Ciuba Jan 15 '15 at 10:02
  • @MichałCiuba but he doesn't.. he uses one MOC per queue so it looks ok so far. The FETCHING and ACCESSING the object might be an issue – Daij-Djan Jan 15 '15 at 10:09
  • Core Data requires one MOC per *thread* if you use `(NSConfinementConcurrencyType`, as in the posted code. A dispatch queue can use *many* threads under the hood. – Michał Ciuba Jan 15 '15 at 10:15
  • A yeah, I fell into that trop myself once. my bad. thanks for the reminder :) – Daij-Djan Jan 15 '15 at 10:55