Основные данные, кэширование NSManagedObjects в NSMutableDictionary, Проблемы - PullRequest
0 голосов
/ 23 марта 2011

Я пишу приложение для словаря и пытаюсь импортировать необработанные данные из строк, по одной строке на слово.Среди прочего, необработанные входные строки содержат названия частей речи, к которым принадлежит соответствующее слово.В моей модели данных у меня есть отдельная сущность для Word s и PartOfSpeech, и я хочу создать одну сущность типа PartOfSpeech для каждой уникальной части речи, которая может присутствовать во входных строках, и установить отношения изWord с соответствующих пар речи.У сущности PartOfSpeech есть только один атрибут, name и отношение один-ко-многим с Word: enter image description here

Моя первая реализация получения уникальных PartOfSpeech сущностей, включающих их кеширование визменяемый массив и его фильтрация каждый раз с помощью предиката.Это работало, но было медленно.Я решил немного ускорить его, кэшируя PartsOfSpeech в NSDictionary, и теперь, когда я пытаюсь сохранить хранилище данных после импорта, я получаю сообщение об ошибке «Невозможно сохранить объекты со ссылками вне их собственных хранилищ».Похоже, проблема в словаре, но как я могу ее решить?

Вот код, который сработал: (в обоих фрагментах managedObjectContext - это ivar, а метод processStringsInBackground: работает на фонес использованием метода performSelectorInBackground:withObject:)

- (void) processStringsInBackground:(NSFetchRequest *)wordStringsReq {
    NSError *err = NULL;    
    NSFetchRequest *req = [[NSFetchRequest alloc] init];
    [req setEntity:[NSEntityDescription entityForName:@"PartOfSpeech" inManagedObjectContext:managedObjectContext]];
    err = NULL;
    NSMutableArray *selectedPartsOfSpeech = [[managedObjectContext executeFetchRequest:req error:&err] mutableCopy];
    NSPredicate *p = [NSPredicate predicateWithFormat:@"name like[c] $name"];
    //  NSPredicate *formNamePredicate = [NSPredicate predicateWithFormat:<#(NSString *)predicateFormat#>]
...
    for (int i = 0; i < count; i++){
...     
        currentPos = [self uniqueEntityWithName:@"PartOfSpeech" usingMutableArray:selectedPartsOfSpeech predicate:p andDictionary:[NSDictionary dictionaryWithObject:partOfSpeech forKey:@"name"]];
...
    }
}

- (NSManagedObject *) uniqueEntityWithName:(NSString *) entityName usingMutableArray:(NSMutableArray *)objects predicate:(NSPredicate *)aPredicate andDictionary:(NSDictionary *) params {
    NSPredicate *p = [aPredicate predicateWithSubstitutionVariables:params];
    NSArray *filteredArray = [objects filteredArrayUsingPredicate:p];
    if ([filteredArray count] > 0) {
        return [filteredArray objectAtIndex:0];
    }
    NSManagedObject *newObject = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:managedObjectContext];
    NSArray *dicKeys = [params allKeys];
    for (NSString *key in dicKeys) {
        [newObject willChangeValueForKey:key];
        [newObject setPrimitiveValue:[params valueForKey:key] forKey:key];
        [newObject didChangeValueForKey:key];
    }
    [objects addObject:newObject];
    return newObject;
}

И здесь то же самое, но с кэшированием с использованием NSMutableDictionary, который впоследствии не удается сохранить:

- (void) processStringsInBackground:(NSFetchRequest *)wordStringsReq {
    NSError *err = NULL;    
    [req setEntity:[NSEntityDescription entityForName:@"PartOfSpeech" inManagedObjectContext:managedObjectContext]];
    NSArray *selectedPartsOfSpeech = [managedObjectContext executeFetchRequest:req error:&err];
    NSMutableDictionary *partsOfSpeechChache = [[NSMutableDictionary alloc] init];
    for (PartOfSpeech *pos in selectedPartsOfSpeech) {
        [partsOfSpeechChache setObject:pos forKey:pos.name];
    }
...
    for (int i = 0; i < count; i++){
...     
        currentPos = [self uniqueEntity:@"PartOfSpeech" withName:partOfSpeech usingDictionary:partsOfSpeechChache];
...
    }
}

- (NSManagedObject *)uniqueEntity:(NSString *) entityName withName:(NSString *) name usingDictionary:(NSMutableDictionary *) dic {
    NSManagedObject *pos = [dic objectForKey:name];
    if (pos != nil) {
        return pos;
    }
    NSManagedObject *newPos = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:managedObjectContext];
    [newPos willChangeValueForKey:@"name"];
    [newPos setPrimitiveValue:name forKey:@"name"];
    [newPos didChangeValueForKey:@"name"];

    [dic setObject:newPos forKey:name];
    return newPos;
}

Не могли бы вы помочь мне найтипроблема?

С уважением, Тимофей.

Ответы [ 3 ]

1 голос
/ 23 марта 2011

Ошибка вызвана формированием взаимосвязи между управляемыми объектами, которые не разделяют одно и то же постоянное хранилище.Это можно сделать следующим образом:

  • Создание управляемого объекта с инициализацией без вставки его в контекст.
  • Удаление управляемого объекта из контекста с сохранением его в другом объекте, например в массиве,и затем установление отношения с ним.
  • Случайное создание двух стеков базовых данных, чтобы у вас было два контекста и два хранилища.
  • Неправильные конфигурации в контексте нескольких хранилищ.

Я не вижу ни одной части предоставленного вами кода, которая могла бы вызвать проблему.

0 голосов
/ 14 сентября 2015

Хорошие ответы, хотя и немного устаревшие.В прекрасной документации отмечается, что основной NSManagedObjectContext никогда не должен использоваться в рабочих потоках.Вместо этого создайте отдельный NSManagedObjectContext, закрытый для работника, используя «основной» MOC в качестве родителя, а затем это вместо этого.Вот соответствующая страница «Параллельность» из Руководства по программированию основных данных:

https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Conceptual/CoreData/Concurrency.html

Фрагмент (Swift)

let jsonArray = … //JSON data to be imported into Core Data
let moc = … //Our primary context on the main queue

let privateMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
privateMOC.parentContext = moc

privateMOC.performBlock {
    for jsonObject in jsonArray {
        let mo = … //Managed object that matches the incoming JSON structure
        //update MO with data from the dictionary
    }
    do {
        try privateMOC.save()
    } catch {
        fatalError("Failure to save context: \(error)")
    }
}
0 голосов
/ 23 марта 2011

Оказывается, что неправильно передавать NSManagedContext в поток, отличный от того, в котором он был создан. Вместо этого нужно передать NSPersistenceStroreCoordinator в другой поток и создать там новый контекст управляемого объекта. Чтобы объединить изменения в «основной» контекст, необходимо сохранить контекст другого потока, получить уведомление о завершении сохранения в главном потоке и объединить изменения (см. Документацию Apple относительно Core Data и параллелизма, can ' дать вам ссылку, потому что я прочитал ее в Xcode). Итак, вот изменения, которые я сделал в своем коде, чтобы он работал (только публикация измененных строк):

&mdash; (void) processStringsInBackground:(NSDictionary *) params { NSFetchRequest *wordStringsReq = [params objectForKey:@"wordStringsReq"]; NSPersistentStoreCoordinator *coordinator = [params objectForKey:@"coordinator"]; NSManagedObjectContext *localContext = [[NSManagedObjectContext alloc] init]; [localContext setPersistentStoreCoordinator:coordinator];

(все ссылки на managedObjectContext были заменены на localContext

И в главном потоке я вызываю этот метод так:

....... NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:req, @"wordStringsReq", persistentStoreCoordinator, @"coordinator", nil]; //the params i pass to the background method [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"NSManagingContextDidSaveChangesNotification" object:nil]; //register to receive the notification from the save [self performSelectorInBackground:@selector(processStringsInBackground:) withObject:dict];</p> <p>} - (void) handleNotification:(NSNotification *) notific { NSLog(@"got notification, %@", [notific name]); [managedObjectContext mergeChangesFromContextDidSaveNotification:notific]; }

Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...