2015-08-25 2 views
1

У меня есть приложение, в котором я загружаю данные при запуске, используя список операций, и он случайно разбивается по неизвестным причинам основных данных, поэтому я потратил несколько дней на проверку лучших практик по обновлению/извлечению данных в многопоточном ядре данные с MagicalRecord. Один из вариантов состоял в том, чтобы включить многопоточный отладчик -com.apple.CoreData.ConcurrencyDebug 1, где Xcode останавливает приложения, когда он нарушает одно из своих правил. Таким образом, Xcode останавливает мое приложение на этой линии [SyncRequestEntity MR_createEntityInContext:[self getPrivateContext]]Основные данные о нарушениях в обработке многопоточности

+ (MagicalRecordVersionNumber) version 
{ 
    return MagicalRecordVersionNumber2_3; 
} 
@implementation NSManagedObjectContext (MagicalRecord) 

+ (NSManagedObjectContext *) MR_context 
{ 
    return [self MR_contextWithParent:[self MR_rootSavingContext]]; 
} 

+ (NSManagedObjectContext *) MR_contextWithParent:(NSManagedObjectContext *)parentContext 
{ 
    NSManagedObjectContext *context = [self MR_newPrivateQueueContext]; 
    [context setParentContext:parentContext]; 
    [context MR_obtainPermanentIDsBeforeSaving]; 
    return context; 
} 

- (void) MR_obtainPermanentIDsBeforeSaving 
{ 
    [[NSNotificationCenter defaultCenter] addObserver:self 
               selector:@selector(MR_contextWillSave:) 
                name:NSManagedObjectContextWillSaveNotification 
                object:self]; 
} 
+ (NSManagedObjectContext *) MR_newPrivateQueueContext 
{ 
    NSManagedObjectContext *context = [[self alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
    MRLogInfo(@"Created new private queue context: %@", context); 
    return context; 
} 

@end 

@implementation MyClass 

    - (NSManagedObjectContext *) getPrivateContext 
    { 
     if (self.privateContext == nil) 
     { 
      self.privateContext = [NSManagedObjectContext MR_context]; 
     } 
     return self.privateContext; 
    } 

    - (SyncRequestEntity *) getSyncRequest 
    { 
     SyncRequestEntity *syncRequest = [SyncRequestEntity MR_findFirstByAttribute:@"key" withValue:self.itemKey inContext:[self getPrivateContext]]; 

     // Checking if the entity was sync previously with the same filters. 
     if (syncRequest == nil) 
     { 
      syncRequest = [SyncRequestEntity MR_createEntityInContext: [self getPrivateContext]]; 
     } 

     return syncRequest; 
    } 
@end 

@implementation NSManagedObject (MagicalRecord) 
+ (id) MR_createEntityInContext:(NSManagedObjectContext *)context 
{ 
    if ([self respondsToSelector:@selector(insertInManagedObjectContext:)] && context != nil) 
    { 
     id entity = [self performSelector:@selector(insertInManagedObjectContext:) withObject:context]; 
     return entity; 
    } 
    else 
    { 
     NSEntityDescription *entity = nil; 
     if (context == nil) 
     { 
      entity = [self MR_entityDescription]; 
     } 
     else 
     { 
      entity = [self MR_entityDescriptionInContext:context]; 
     } 

     if (entity == nil) 
     { 
      return nil; 
     } 

     return [[self alloc] initWithEntity:entity insertIntoManagedObjectContext:context]; 
    } 
} 
@end 

privateContext является локальной переменной для каждой операции, так что я есть отдельные контексты для каждой операции, чтобы не прерывать главной. Дело в том, что я создаю один закрытый контекст для каждого потока, и я просто пытаюсь создать новый экземпляр NSManagedObject, используя этот контекст, и Xcode говорит, что я нарушаю правила обработки данных с несколькими потоками. Кто-нибудь знает, что происходит?

+0

Что такое код из 'MR_context'? Вы принимали во внимание, что блоки могут работать на разных потоках? –

+0

Добавления деталей @ AminNegm-Awad. Я не использую блок здесь. – Maystro

+0

Какую версию MagicalRecord вы используете? – casademora

ответ

0

У нас такая же проблема при разработке собственного приложения.

При попытке выполнить операцию записи в потоке, отличном от контекстного, он иногда падает.

Наше решение заключалось в том, чтобы сделать личного менеджера в файле AppDelegate.m. Просто добавьте этот код:

- (NSManagedObjectContext *)getPrivateManagedObjectContext 
{ 
    if (self.managedObjectContext != nil) { 
     return self.managedObjectContext; 
    } 

    NSPersistentStoreCoordinator *coordinator = [self getPersistentStoreCoordinator]; 
    if (coordinator != nil) { 
     self.managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
     [self.managedObjectContext setPersistentStoreCoordinator:coordinator]; 
    } 
    return self.managedObjectContext; 
} 

Затем, когда вам нужно выполнить любую операцию, вы должны использовать этот метод, который обеспечивает блок работает на том же потоке контекста:

[self.managedObjectContext performBlock:^{...}]; 
[self.managedObjectContext performBlockAndWait:^{...}]; 
+0

Зачем мне нужно использовать performBlock? Я уже на фоновом потоке (NSOperation) Я просто хочу создавать/извлекать/вставлять сущности, используя privateContext, который я создаю для каждой операции, но, похоже, я нарушаю одно из своих правил, которое я пока не понимаю. – Maystro

+0

Это решение не использует MagicalRecord ... – casademora

+0

Проблема в том, что если вы выполняете операции одновременной записи, это может привести к сбою. Вам нужно использовать только один закрытый контекст и выполнять каждую операцию в одном и том же контексте. –

Смежные вопросы