2014-01-02 2 views
0

При попытке узнать больше о Core Data и многопоточности я написал приложение, которое не удалось сохранить в основных данных. Сначала некоторые фон. Перед тем, как появится мое представление, я создаю управляемый контекст. Это происходит в основной поток:Основные данные Управляемый контекст не сохраняется в многопоточной среде

- (void)viewWillAppear:(BOOL)animated 
{ 
    [super viewWillAppear:animated]; 
    if (!self.managedObjectContext) [self useDocument]; 

} 

- (void)useDocument 
{ 
    NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; 
    url = [url URLByAppendingPathComponent:MY_DOCUMENT]; 
    UIManagedDocument *document = [[UIManagedDocument alloc] initWithFileURL:url]; 

    if (![[NSFileManager defaultManager] fileExistsAtPath:[url path]]) { 
     [document saveToURL:url 
      forSaveOperation:UIDocumentSaveForCreating 
      completionHandler:^(BOOL success) { 
       if (success) { 
        NSLog(@"@@@AMRO--->managedObjectContext has been setup"); 
        self.managedObjectContext = document.managedObjectContext; 
        [self refresh]; 
       } 
      }]; 
    } else if (document.documentState == UIDocumentStateClosed) { 
     [document openWithCompletionHandler:^(BOOL success) { 
      if (success) { 
       NSLog(@"@@@AMRO--->managedObjectContext has been opened"); 
       self.managedObjectContext = document.managedObjectContext; 
       [self refresh]; 
      } 
     }]; 
    } else { 
     NSLog(@"@@@AMRO--->managedObjectContext has been set"); 
     self.managedObjectContext = document.managedObjectContext; 
    } 
} 

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

 dispatch_queue_t fetchQ = dispatch_queue_create("Client Fetch", NULL); 
     dispatch_async(fetchQ, ^{ 
      [self.managedObjectContext performBlock:^{ 
       dispatch_async(dispatch_get_main_queue(), ^{ 
        [self reload]; 
        dispatch_async(fetchQ, ^{ 
         [self refreshClientInformation]; 
        }); 
       }); 
      }]; 
     }); 

вызова к refreshClientInformation, занимает около 2 минут, чтобы получить информации, однако, я хочу представление для загрузки в то же время с тем, что у меня есть . Вышеупомянутый подход не позволяет мне быть сохраненным в контексте, несмотря на явный вызов [self.managedContext save:&error] в refreshClientInformation после каждого обновления данных клиента. refreshPersonalClientInformations выполняется в очереди потоков «Клиент Fetch».

- (void) refreshPersonalClientInformation 
{ 
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"CLIENT"]; 
    request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"stationId" ascending:YES]]; 
    NSDate *twoHoursAgo = [NSDate dateWithTimeIntervalSinceNow:-2*60*60]; 
    request.predicate = [NSPredicate predicateWithFormat:@"conditionsUpdated < %@", twoHoursAgo]; 
    NSError *error = nil; 

    int counter = 0; 

    // Execute the fetch 
    NSArray *matches = [self.managedObjectContext executeFetchRequest:request error:&error]; 

    // Check what happened in the fetch 
    if (!matches) { // nil means fetch failed; 
     NSLog(@"ERROR: Fetch failed to find CLIENT %@ in database", DEFAULT); 
    } else if (![matches count]) { // none found, so lets retrieve it below 
     return; 
    } else { 
     for (CLIENT *client in matches) { 
      NSDictionary *clientDict = [ClientFetcher clientInfo:client.clientId]; 

      client.name = [clientDict[NAME] description]; 

      client.age = [clientDict[AGE] description]; 
      client.updated = [NSDate date]; 
      if (![self.managedObjectContext save:&error]) { 
       NSLog(@"@@@@@@@@@@@@@@@@@@@@@@@@@@AMRO Unresolved error %@, %@", error, [error userInfo]); 
      } 

      //Have to rate limit requests at no more than 10 per minute. 
      NSLog(@"Sleeping for 6s"); 
      sleep(6); 
      NSLog(@"Done sleeping for 6s"); 
     } 
    } 
} 

Я даже попытался запустить сохранить в refreshPersonalClientInfo делая это:

dispatch_async(dispatch_get_main_queue(), ^{ 
    [self.managedObjectContext save:nil]; 
}); 

Но это не сработало, никаких ошибок не было обнаружено

В качестве эксперимента я изменил вызов резьбы на это, когда появляется мое представление, и основные данные ядра в конечном итоге сохраняются, проблема в том, что мое представление заблокировано, и мне нужно долго ждать, пока данные получаются. Кроме того, когда я говорю, в конце концов, я имею в виду, что это не произойдет после того, как я называю [self.managedContext save:nil], но через несколько минут:

 dispatch_queue_t fetchQ = dispatch_queue_create("Client Fetch", NULL); 
     dispatch_async(fetchQ, ^{ 
      [self.managedObjectContext performBlock:^{ 
       [self refreshPersonalClientInformation]; 
       dispatch_async(dispatch_get_main_queue(), ^{ 
        [self reload]; 
       }); 
      }]; 
     }); 

Любые предложения о том, как я должен получить мой informaion в фоновом потоке и быть в состоянии сохранить данные на диск, пока я до сих пор оставляю свой пользовательский интерфейс доступным?

+1

Посмотрите здесь, http://www.cocoanetics.com/2012/07/multi-context-coredata/ – Retro

+1

Поскольку Аарон сказал, что вы должны сделать «детский контекст, чтобы выполнить все в фоновом режиме», как рекомендует Apple, «один ManagedObjectContext не должен использоваться несколькими потоками». – spider1983

+0

Когда вы открываете UIManagedDocument, открытие или сохранение выполняются асинхронно, поэтому вам нужно убедиться, что вы не пытаетесь использовать managedObjectContext до вызова обработчика завершения. Ознакомьтесь с этой ссылкой для получения более подробной информации о ожидании завершения фоновых действий до включения пользовательского интерфейса. В этом примере речь идет об импорте фона из iCloud так же, как и в вашем случае использования. http://ossh.com.au/design-and-technology/software-development/uimanageddocument-icloud-integration/when-to-enable-the-user-interface/ –

ответ

1

Вы лучше создать контекст ребенка выполнять все в фоновом режиме:

dispatch_queue_t fetchQ = dispatch_queue_create("Client Fetch", NULL); 
    dispatch_async(fetchQ, ^{ 
     NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
     context.parentContext = self.managedObjectContext; 
     [context performBlock:^{ 
      [self refreshPersonalClientInformation]; 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       [self reload]; 
      }); 
     }]; 
    }); 
+0

Спасибо, я сделаю это. Я должен прочитать о детском/родительском контексте, чтобы понять принципала позади этого. –

+1

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

+0

@ Контекст «родительский/дочерний» аарона был действительно ошибкой, особенно в многопоточной среде.Таким образом, ваше сопротивление было хорошим поведением программистов :-) Но, возможно, что-то, вероятно, было исправлено с текущей версией iOS (7), поэтому мне тоже нужно дать еще один шанс :-) – Leonardo

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