2012-08-25 42 views
0

我使用以下代碼從Web服務加載數據並將其存儲在Core Data數據庫中。CoreData和Threading

dispatch_queue_t fetchQ = dispatch_queue_create("fetcher", NULL); 
dispatch_async(fetchQ, ^{ 
    NSArray *list = [WebService loadData]; 
    dispatch_sync(dispatch_get_main_queue(), ^{ 
     if (list != nil) { 
      for (NSDictionary *data in list) { 

       NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Object" inManagedObjectContext:appDelegate.coreDataDatabase.managedObjectContext]; 
       NSFetchRequest *request = [[NSFetchRequest alloc] init]; 
       [request setEntity:entityDescription]; 

       NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id = %i", [[data objectForKey:@"id"] integerValue]]; 
       [request setPredicate:predicate]; 

       NSError *error = nil; 
       NSArray *data = [appDelegate.coreDataDatabase.managedObjectContext executeFetchRequest:request error:&error]; 

       Object *object = [data objectAtIndex: 0]; 
       if (object == nil) { 
        object = [NSEntityDescription insertNewObjectForEntityForName:@"Object" inManagedObjectContext:appDelegate.coreDataDatabase.managedObjectContext]; 
        object.id = [NSNumber numberWithInteger:[[data objectForKey:@"id"] integerValue]]; 
       } 

       object.name = [data objectForKey:@"name"]; 
      } 

      [appDelegate.coreDataDatabase saveToURL:appDelegate.coreDataDatabase.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:nil]; 
     } 
    }); 
}); 
dispatch_release(fetchQ); 

該代碼是簡化的,但它應該代表序列。對Web服務的訪問在後臺運行。之後,商店在主線程中被調用。實際上應該沒問題吧?

不幸的是,整個作品不是在所有設備上。在我的所有設備和大多數其他設備上,沒有任何問題。 在一些,但顯然是如此,因爲不幸的是,商店裏有一些負面的Bewertugnen和一些問題。 的問題是,他發現一個已經存在的記錄在數據庫中,而不是不斷有新的投資

感謝您的幫助, 斯特凡

+0

,如果你明確的最後一句話這將有助於。我不明白那一部分。 –

回答

1

我很難理解你的實際問題。我不完全相信你的意思是這個...

在某些但顯然如此,因爲很不幸,在商店和一對夫婦的問題,有一些 負Bewertugnen。該 問題是,他發現了一個已經存在的記錄在數據庫中,而不是 不斷有新的投資

所以我把我最好的猜測。

從代碼中,我假設你正在使用UIManagedDocument。

您不應該直接保存UIManagedDocument(saveToUrl)。相反,你應該讓它處理自動保存,因爲它會適當地做到這一點。

如果您的NSUndoManagerUIManagedDocument上有效,則不需要執行任何操作。所有操作將自動保存。如果你沒有撤銷管理器,你需要戳一下它才能知道它有髒數據。你這樣做有:

[managedDocument updateChangeCount:UIDocumentChangeDone]; 

但是請注意,UIManagedDocument使用嵌套的上下文,並有相關的插入新對象了幾個錯誤。也就是說,持久性標識未正確推回到子上下文。我對你的問題的假設是你因此得到重複的數據條目。

這個SO問題在一定程度上解決了這個問題。

Core Data could not fullfil fault for object after obtainPermanantIDs

基本上,你有幾種選擇,考慮到你要繼續UIManagedDocument。首先,獲取永久ID。替換此行:

[appDelegate.coreDataDatabase saveToURL:appDelegate.coreDataDatabase.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:nil]; 

本(當然 - 處理錯誤 - 這只是傳遞0):

NSManagedObjectContext *moc = appDelegate.coreDataDatabase.managedObjectContext; 
[moc obtainPermanentIDsForObjects:moc.insertedObjects.allObjects error:0]; 
[appDelegate.coreDataDatabase updateChangeCount:UIDocumentChangeDone]; 

還存在一些問題,這一點,但他們是非常罕見的。你的代碼似乎沒有非常複雜的對象圖,所以這應該就足夠了。

另一種選擇是導入到一個單獨的MOC(UIManagedDocument父上下文的孩子),並且只需在導入完成時重新獲取主上下文。

使用您的代碼示例,一個簡單的方法來做到這一點會是這樣的......

dispatch_queue_t fetchQ = dispatch_queue_create("fetcher", NULL); 
dispatch_async(fetchQ, ^{ 
    NSArray *list = [WebService loadData]; 
    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init]; 
    moc.parentContext = appDelegate.coreDataDatabase.managedObjectContext.parentContext; 
    for (NSDictionary *data in list) { 
     NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Object" inManagedObjectContext:moc]; 
     NSFetchRequest *request = [[NSFetchRequest alloc] init]; 
     [request setEntity:entityDescription]; 

     NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id = %i", [[data objectForKey:@"id"] integerValue]]; 
     [request setPredicate:predicate]; 

     NSError *error = nil; 
     NSArray *data = [moc executeFetchRequest:request error:&error]; 
     Object *object = [data objectAtIndex: 0]; 
     if (object == nil) { 
      object = [NSEntityDescription insertNewObjectForEntityForName:@"Object" inManagedObjectContext:moc]; 
      object.id = [NSNumber numberWithInteger:[[data objectForKey:@"id"] integerValue]]; 
     } 

     object.name = [data objectForKey:@"name"]; 
    } 

    // Save the MOC you just loaded everything into 
    [moc save:&error]; // handle error... 
    // Save the parent MOC, which is also the parent of your main MOC 
    moc = moc.parentContext; 
    [moc performBlock:^{ 
     [moc save:&error]; // handle error 
     // When all the saves are done, run on the main queue and just refetch 
     // all the entities that were inserted. 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      // Here, use appDelegate.coreDataDatabase.managedObjectContext 
      // to refetch the objects that were altered. If you are using a 
      // table view or FRC just issue a refetch of the data and the 
      // main MOC will get everything that was modified. 
     }); 
    }]; 
}); 
dispatch_release(fetchQ); 
+0

這似乎工作:)我的第一個測試正在工作的設備之前不工作... – Urkman

+0

在你鏈接的文章中,你正在談論一個已知的錯誤。我在哪裏可以找到更多關於此錯誤的信息謝謝:) – Urkman

+0

不幸的是,報告給蘋果的錯誤沒有公開。谷歌搜索,但是,應該爲您提供大量的相關點擊。然而,在這種情況下,我認爲這篇文章解釋得非常透徹。 –

0

如果您創建的主線程的上下文,則每次訪問它也必須位於主線上(假設pre-ios 5配置 - Core Data支持iOS 5之後的3個模式)。

您可以創建一個串行調度隊列,在那裏創建moc,並通過該隊列管理所有對它的訪問。分派隊列序列化訪問,所以你永遠不會有併發。

我自己只是轉換爲在iOS5中使用私有隊列功能,在這種情況下,每次訪問都是通過'[moc perform block ...]'完成的 - 我花了不到1個小時的時間進行轉換,而實際的代碼更清潔(我已經使用了前面提到的串行隊列)。