Разработка iOS-приложения с несколькими параллельными доступами к базе данных - PullRequest
1 голос
/ 17 января 2012

В моем приложении я получаю запросы с содержимым JSON, анализирую их и сохраняю в CoreData. В то же время пользователь взаимодействует с БД (доступ для чтения и записи).

После сохранения данных в БД запускается вторая задача, которая создает новые данные на основе полученных данных. Я собираюсь использовать Grand Central Dispatch для анализа и хранения данных в БД.

Моя проблема заключается в том, что при использовании GCD я получаю EXC_BAD_ACCESS, что может быть связано с неосновной безопасностью Core Data, которую я предполагаю. Другая ошибка заключается в том, что я получаю тупик при использовании контекста performBlockAndWait.

Как мне разработать свое приложение, которое правильно обрабатывает GCD и NSMutableContexts?

------- EDIT --------

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

Мое приложение в настоящее время структурировано следующим образом: у меня есть пара менеджеров, каждый из которых имеет свой собственный контекст. Но при использовании нескольких потоков я дохожу до того, что 3 потока вызывают один и тот же Менеджер, что означает, что один контекст используется 3 потоками одновременно. Это приводит к тупику.

Чтобы решить эту проблему, я пришел к мысли создать контекст по имени потока, например:

- (NSManagedObjectContext *)createManagedObjectContextWithTreadName:(NSString*) threadname {

    if([NSThread currentThread].name.length ==0){
        [NSThread currentThread].name = threadname; 
    }

    NSManagedObjectContext *context = nil;
    context = [self.contextStore objectForKey:threadname];

    if(!context){
        NSLog(@"Creating context for threadname: %@",threadname);

        NSPersistentStoreCoordinator *coordinator = self.persistentStoreCoordinator;
        if (coordinator != nil)
        {
            context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
            context.persistentStoreCoordinator = coordinator;
            context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;

            NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
            [nc addObserver:self selector:@selector(mergeChangesFromMOC:) name:NSManagedObjectContextDidSaveNotification object:context];
            [self.contextStore setValue:context forKey:threadname];
        }
    }
    return context;
}

Это хорошая идея?

Ответы [ 2 ]

3 голосов
/ 17 января 2012
2 голосов
/ 19 января 2012

Хорошо, вот как я это решил:

+(NSManagedObjectContext *)managedObjectContext {
    AppDelegate *delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
    NSManagedObjectContext *moc = delegate.managedObjectContext;

    NSThread *thread = [NSThread currentThread];

    if ([thread isMainThread]) {
        return moc;
    }

    // a key to cache the context for the given thread
    NSString *threadKey = [NSString stringWithFormat:@"%p", thread];

    // delegate.managedObjectContexts is a mutable dictionary in the app delegate
    NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;

    if ( [managedObjectContexts objectForKey:threadKey] == nil ) {
        // create a context for this thread
        NSManagedObjectContext *threadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 

        threadContext.persistentStoreCoordinator = [moc persistentStoreCoordinator];
        threadContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;

        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
        [nc addObserver:self selector:@selector(mergeChangesFromMOC:) name:NSManagedObjectContextDidSaveNotification object:threadContext];

        // cache the context for this thread
        [managedObjectContexts setObject:threadContext forKey:threadKey];
        //NSLog(@"MocCount: %d",managedObjectContexts.count);
    }

    return [managedObjectContexts objectForKey:threadKey];
}

+ (void)mergeChangesFromMOC:(NSNotification *)aNotification {

    //NSLog(@"Performing a merge of managed object context in class %@",[self class]);

    @try {
        AppDelegate *delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
        NSManagedObjectContext *moc = delegate.managedObjectContext;
        [moc mergeChangesFromContextDidSaveNotification:aNotification];


        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
        [nc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:[aNotification object]];      
    }
    @catch (NSException * e) {
        NSLog(@"Stopping on exception: %@", [e description]);
    }
    @finally {}
}

Везде, где мне нужен контекст, я спрашиваю [ConistingClass managedObjectContext] и получаю контекст для основного потока или потока, в котором я нахожусь. Когда необходимо объединение, это выполняется в основном потоке.

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