2

我正在構建一個應用程序,它從遠程服務器提取數據並將它們存儲在CoreData SQLite數據庫中。當主線程使用它時,我從後臺線程獲取數據。以下是我正在使用的主要方法。核心數據和多線程崩潰保存

  1. 所有後臺線程都有自己NSManagedObjectContext
  2. persistentStoreCoordinator在上下文之間共享。
  3. 在後臺線程的每次提取上,我保存:插入的對象並重置:本地上下文。
  4. 使用通知我使用主線程上下文合併。

我遇到的問題是:

  • 隨機我越來越崩潰,並沒有消息(NSZombie和崩潰斷點設置)在後臺線程保存:操作。
  • 有很多重複的數據正在生成。 Web服務數據肯定沒問題,所以在服務器端沒有問題。

這是代碼。

的AppDelegate中

- (void)applicationDidBecomeActive:(UIApplication *)application 
{ 
    // Update data from remote server 
    WebserviceDataModel *webservice = [[WebserviceDataModel alloc] init]; 
    webservice.managedObjectContext = self.managedObjectContext; 
    [webservice startImport]; 
} 

後臺線程取和保存數據上WebserviceDataModel

- (void)startImport 
{ 
    dispatch_queue_t downloadQueue = dispatch_queue_create("startImport in WebserviceDataModel", NULL); 
    dispatch_async(downloadQueue, ^{ 
     NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init]; 
     moc.persistentStoreCoordinator = self.managedObjectContext.persistentStoreCoordinator; 

     // get the remote data 
     NSDictionary *lojas = [Loja allLojasFromRemoteServer]; 

     for (NSDictionary *lojaInfo in lojas) { 
      Loja *loja __attribute__((unused)) = [Loja lojaWithRemoteData:lojaInfo inManagedObjectContext:moc]; 
     } 

     if ([moc hasChanges]) { 
      [moc save:nil]; 
      [moc reset]; 
     } 
     [moc release];   

    }); 
    dispatch_release(downloadQueue); 
} 

的NSManagedObject方法爲對象創建:+ (Loja *)lojaWithRemoteData:inManagedContext:

+ (Loja *)lojaWithRemoteData:(NSDictionary *)remoteData inManagedObjectContext:(NSManagedObjectContext *)context 
{ 
    Loja *loja = nil; 

NSFetchRequest *request = [[NSFetchRequest alloc] init]; 
request.entity = [NSEntityDescription entityForName:@"Loja" inManagedObjectContext:context]; 
request.predicate = [NSPredicate predicateWithFormat:@"lojaId = %d", [[remoteData objectForKey:@"lojaId"] intValue]]; 

NSError *error = nil; 
loja = [[context executeFetchRequest:request error:&error] lastObject]; 
[request release]; 

if (!error && !loja) { 
     // create the record 
     loja = [NSEntityDescription insertNewObjectForEntityForName:@"Loja" inManagedObjectContext:context]; 

     loja.lojaId = [remoteData objectForKey:@"lojaId"]; 
     loja.email = [remoteData objectForKey:@"email"]; 
     loja.facebook = [remoteData objectForKey:@"facebook"]; 
     // ... and others... 
    } 

    return loja; 
} 

認購到NSManagedObjectContextDidSaveNotification上WebserviceDataModel

- (id)init 
{ 
    self = [super init]; 
    if (self) { 
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:nil]; 
    } 
    return self; 
} 

的contextChanged:方法上WebserviceDataModel

- (void)contextChanged:(NSNotification*)notification 
{ 
    if ([notification object] == self.managedObjectContext) return; 

    if (![NSThread isMainThread]) { 
     [self performSelectorOnMainThread:@selector(contextChanged:) withObject:notification waitUntilDone:YES]; 
     return; 
    } 
    [[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification]; 
} 
+0

嗯,我已經知道了。現在它的功能就像魅力一樣。多線程上下文的實現是正確的。問題出在'applicationDidBecomeActive:'我的應用程序使用CoreLocation Fence來獲取功能列表。當應用程序第一次啓動時,CoreLocation框架會向用戶顯示警告消息,說明應用程序使用de用戶位置...該警報再次調用「applicationDidBecomeActive:」,創建兩個併發的更新波。只是將我的** WebserviceDataModel **移動到一個屬性並實現了一個標誌,以瞭解它是否正在運行。而已。 –

回答

1

嗯,我已經想通了。現在它的功能就像魅力一樣。多線程上下文的實現是正確的。問題出在applicationDidBecomeActive:我的應用程序使用CoreLocation Fence獲取功能列表。當應用程序第一次啓動時,CoreLocation框架會向用戶顯示警告消息,說明該應用程序使用de用戶位置...該警報再次呼叫applicationDidBecomeActive:,從而創建兩個併發的更新波。只是將我的WebserviceDataModel移動到一個屬性並實現了一個標誌來知道它是否運行。這就是它

只是爲了最終的改進,將合併策略更改爲NSMergeByPropertyStoreTrumpMergePolicy,所以現在服務器端數據(在內存中)勝過本地存儲。

0

沒有任何細節是很難理解到底是怎麼回事。

說這個,首先,嘗試設置斷點並遵循應用程序流程。也許你可以找到你的應用崩潰的地步。

那麼,你確定lojaId是一個標量值嗎?當你創建一個新的實體,您寫道:

loja.lojaId = [remoteData objectForKey:@"lojaId"]; 

也許這可能是錯誤的,所以我會嘗試以下斷言:

[NSPredicate predicateWithFormat:@"lojaId == %@", [remoteData objectForKey:@"lojaId"]]; 

最後,當你做一個保存嘗試日誌NSError對象。也許你可以找到你的崩潰的原因。

if ([moc hasChanges]) { 

    NSError* error = nil; 
    [moc save:&error]; 

    if(error) { 

     NSLog(@"Error during save: %@\n%@", [error localizedDescription], [error userInfo]); 
     abort(); // only for debug purposes 
    } 
    [moc reset]; 
} 

希望它有幫助。

+0

loja.lojaId是一個NSNumber,所以用這種格式來裝載謂詞。我已經發現了這個問題。檢查問題的評論。謝謝!! –

+0

@GeorgeVillasboas好的沒問題。將您的解決方案作爲anwer發佈。謝謝。 –