Переключение между хранилищами Core Data

В настоящее время я обновляю приложение для использования Core Data. Приложение, которое вы могли бы назвать «просмотрщиком базы данных», одновременно может просматривать только одну базу данных. Каждая база данных хранится в отдельной папке. В настоящее время данные загружаются и хранятся в виде набора файлов plist.

В новой версии мне нужно преобразовать эти базы данных plist в хранилища Core Data (по одному хранилищу для каждой базы данных). Я уже настроил методы, создающие отдельные файлы хранилища, и создаю объекты. Проблема в том, что все объекты сохраняются в первую созданную мной базу данных, а не в «текущий» или «последний созданный» файл.

Основной процесс, который я использую:

//For each database {
//Create the sqlite file and set up NSManagedObjectContext
[MagicalRecord setupCoreDataStackWithStoreNamed:
    [NSURL fileURLWithPath:
    [NSString stringWithFormat:@"%@/%@/%@.sqlite",
    dirPath, directory, directory]]];
NSManagedObjectContext *managedObjectContext = 
    [NSManagedObjectContext MR_contextForCurrentThread];

//Iterate through all the plist files and create the necessary entities.
//Save new entities to file
[managedObjectContext MR_save];
//Clean up all cashes
[MagicalRecord cleanUp];
}

Как правильно переключаться между хранилищами, по сути «сбрасывая» все между каждым переключением. Предпочтительно (если возможно) с использованием магической записи.

РЕДАКТИРОВАТЬ: я обнаружил часть проблемы и удалил большую часть нежелательного поведения. Оказывается, вы не можете надежно вызвать [MagicalRecord cleanUp] в фоновом потоке. Кроме того, он не делает того, что, по моему мнению, должен (см. ниже). В итоге я перезванивал в основной поток после каждого «сохранения», чтобы сбросить стек Core Data. При этом создается новый контекст для первых трех баз данных. после этого он дублирует контекст из базы данных трехлетней давности. Таким образом, в цикле используются те же три контекста.

Это то, что у меня есть сейчас; Я начинаю процесс с создания фонового потока и запускаю код для создания одной базы данных в фоновом режиме:

backgroundQueue = dispatch_queue_create("com.BrandonMcQuilkin.myQueue", NULL);
    dispatch_async(backgroundQueue, ^(void) {
        [self createSQLiteDatabase:updateList];
    });

Затем создаем стек и базу данных:

- (void)createSQLiteDatabase:(NSArray *)updateList
{
    NSString *directory = [updateList objectAtIndex:0];
    [MagicalRecord setupCoreDataStackWithStoreNamed:
        [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@/%@.sqlite",
        dirPath, directory, directory]]];
    NSManagedObjectContext *managedObjectContext = 
        [NSManagedObjectContext MR_contextForCurrentThread];
    //Check to see if the stack has reset
    NSLog(@"Before:%i", [[Competition MR_findAllInContext:managedObjectContext] count]);

    //Create and add entities to context...

    //Prepare for next loop
    NSLog(@"After:%i", [[Competition MR_findAllInContext:managedObjectContext] count]);
    [managedObjectContext MR_saveNestedContexts];
    [NSManagedObjectContext MR_resetContextForCurrentThread];

    NSMutableArray *temp = [[NSMutableArray alloc] initWithArray:updateList];
    [temp removeObjectAtIndex:0];

    dispatch_async(dispatch_get_main_queue(), ^(void){
        [self shouldContinueUpdating:temp];
    });

Затем сбросьте все и повторите для всех баз данных:

- (void)shouldContinueUpdating:(NSArray *)databases
{
    //preform cleanup on main thread and release background thread
    [MagicalRecord cleanUp];
    dispatch_release(backgroundQueue);

    if ([databases count] != 0) {
        backgroundQueue = dispatch_queue_create("com.BrandonMcQuilkin.myQueue", NULL);
        dispatch_async(backgroundQueue, ^(void) {
            [self createSQLiteDatabase:databases];
        });
    }
}

С двумя NSLogs я получаю это в консоли: (используя шесть баз данных, шаблон один и тот же, независимо от того, сколько баз данных я конвертирую).

//First Loop
Before:0
After:308
//Second Loop
Before:0
After:257
//Third Loop
Before:0
After:37
//Fourth Loop
Before:308 
After:541 
//Fifth Loop
Before:257
After:490
//Sixth Loop
Before:37
After:270
... Keep adding to each of the three contexts.

И [MagicalRecord cleanUp] не делает того, о чем говорит. Вот что должен делать этот метод.

+ (void) cleanUpStack;
{
[NSManagedObjectContext MR_cleanUp];
[NSManagedObjectModel MR_setDefaultManagedObjectModel:nil];
[NSPersistentStoreCoordinator MR_setDefaultStoreCoordinator:nil];
[NSPersistentStore MR_setDefaultPersistentStore:nil];
}

Но получается, что NSStoreCoordinator каждый раз, когда я сохраняю, является одним и тем же координатором, в одной и той же ячейке памяти, и каждый магазин зависает. Что-то работает не так...


person Brandon Mcq    schedule 03.10.2012    source источник
comment
Мне трудно понять, зачем вам вообще нужны CoreData. Создайте NSDictionary из списков и просто используйте NSDictionary для обслуживания данных. CoreData может делать то, что вам нужно, но это потребует много дополнительной работы, которая в конечном итоге не принесет никакой пользы.   -  person sosborn    schedule 03.10.2012
comment
Я сделал образец, в котором я замерил время, необходимое для сортировки и отображения набора сущностей в образце базы данных. Базовые данные были значительно быстрее, чем загрузка и сортировка NSArrays, которые я получаю из списков. Кроме того, благодаря тому, как CD кэширует данные, это также уменьшило объем памяти, занимаемой сэмплом.   -  person Brandon Mcq    schedule 03.10.2012
comment
В таком случае, могли бы вы создать объект с именем что-то вроде базы данных, а затем переключаться между этими объектами, а не переключаться между хранилищами? Это должно облегчить вам задачу.   -  person sosborn    schedule 03.10.2012
comment
Это то, что я начал делать, но со своим тестом понял, что если я хочу найти все объекты: someEntity, он будет искать во всех базах данных, если у меня их несколько в одном магазине. Я мог бы добавить предикат, чтобы получить те, которые принадлежат базе данных A, но зачем мне искать во всех базах данных? Это просто замедлит работу приложения, тем более что я могу иметь дело с тысячами сущностей в одной базе данных.   -  person Brandon Mcq    schedule 03.10.2012
comment
но зачем мне искать по всем базам данных? - Вам действительно не пришлось бы этого делать, если бы ваши отношения были выстроены правильно. myDatabase.entityAObjects будет возвращать объекты EntityA, принадлежащие myDatabase. Без извлечения и без предиката. Конечно, не зная схемы, я не могу дать лучшего ответа (и этот может быть не совсем точным в вашем случае), но я бы подошел именно так.   -  person sosborn    schedule 03.10.2012
comment
Проблема в том, что мне нужно использовать NSFetchedResultsControllers для ограничения данных, загружаемых в память, плюс быстрее использовать NSFetchRequest с NSPredicate, чем загружать каждый экземпляр одного объекта и сортировать его с помощью оператора forin. @sosborn, я выяснил еще кое-что и сузил проблему, как вы думаете, вы можете взглянуть еще раз? Спасибо!   -  person Brandon Mcq    schedule 03.10.2012
comment
чем загружать каждый экземпляр одной сущности. Вам следует прочитать документы основных данных, потому что это не то, что происходит при загрузке отношений. Тем не менее, использование NSFetchedResultsContoller не помешает вам делать то, что вы хотите.   -  person sosborn    schedule 04.10.2012


Ответы (2)


MagicalRecord может оказаться для вас не лучшим инструментом для этой работы...

Во-первых, давайте исправим использование вами метода setupCoreDataStackWithStoreNamed:. Параметр принимает NSString, а не URL-адрес или путь к файлу. MagicalRecord выберет для вас правильный путь и создаст там ваш магазин. ваш результирующий файл sqlite, скорее всего, будет назван так, как вы предполагали.

Далее вам нужно будет динамически создать модель CoreData для этого файла. Это довольно сложно, но возможно. Вам нужно будет просмотреть эти файлы plist и интерпретировать сущности, атрибуты и отношения, а также создать соответствующие NSEntityDescriptions, NSAttributeDescriptions и NSRelationshipDesctiptions и заполнить NSManagedObjectModel "вручную". Вы хотите найти метод

- [NSManagedObjectModel setEntities:(NSArray *)]

а также методы создания для NSEntityDescription, NSAttributeDescription и NSRelationshipDescription.

Вы также захотите где-нибудь сохранить эту модель, чтобы вам не приходилось каждый раз создавать ее заново. К счастью, он соответствует NSCoding, так что вы сможете просто сохранить его на диск.

После этого вы, вероятно, захотите заполнить свои данные. Отсюда MagicalRecord может вам помочь. Я предлагаю просмотреть блог Importing Data Made Easy. сообщение, которое я написал для Какао — моя девушка

Если вы хотите «переключить хранилище», что, я думаю, означает, что вы хотите создать новое хранилище для каждого файла plist, который у вас есть, тогда вам придется разорвать весь стек Core Data для каждого файла. Если вам удастся использовать MagicalRecord для этого проекта, вам нужно будет просмотреть [MagicalRecord cleanUp] и начать заново. Если бы все модели были одинаковыми, вы могли бы обойтись выпуском постоянного координатора хранилища и созданием нового для своего магазина. Но поскольку ваши «схемы», вероятно, будут другими, вам просто захочется все стереть и начать заново.

person casademora    schedule 03.10.2012
comment
setupCoreDataStackWithStoreNamed: просматривает класс ввода, если это URL-адрес, он открывает/создает базу данных по этому URL-адресу. Если вы используете строку, она может найти базы данных только в корне папки документов, а не во вложенных папках. Я могу создавать файлы sqlite и создавать объекты в порядке. Проблема, с которой я столкнулся, это смена магазинов. Глядя на [очистку MagicalRecord]; он должен разрушить весь стек Core Data. Но что-то сохраняется, так что используется старый файл хранилища, а не новый, который я только что создал. Если мне просто нужно создать файл sql для одной базы данных, отлично... несколько = сбой - person Brandon Mcq; 03.10.2012

Оказывается, у меня возникла проблема из-за ошибки в MagicalRecord. Я представил проблему git здесь: https://github.com/magicalpanda/MagicalRecord/issues/ 270

person Brandon Mcq    schedule 10.10.2012