2017-07-18 45 views
0

我有兩個UIViewController S IN一個標籤欄訪問ViewContext在多個線程導致崩潰

TabBar的一個我正在使用AFNetworking API調用和該API調用在CoreData保存數據。

這裏是我的代碼

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

     for (int i = 0; i < cartList.count; i++) 
     { 
      NSDictionary *dict = [cartList objectAtIndex:i]; 
      NSFetchRequest *request = [Orders fetchRequest]; 
      request.predicate = [NSPredicate predicateWithFormat:@"orderId = %@", [dict objectForKey:kiD]]; 

      NSError *error = nil; 
      NSArray *itemsList = context executeFetchRequest:request error:&error]; 
      if (itemsList.count == 0) 
      { 
       Orders *order = [NSEntityDescription insertNewObjectForEntityForName:@"Orders" inManagedObjectContext:appDel.persistentContainer.viewContext]; 
       [order updateWithDictionary:dict]; 
       order.isNew = NO; 
      } 
      else 
      { 
       Orders *order = [itemsList objectAtIndex:0]; 
       [order updateWithDictionary:dict]; 
       order.isNew = NO; 
      } 
     } 

     dispatch_async(dispatch_get_main_queue(), ^{ 

      [appDel saveContext]; 
      [self refreshValues:NO]; 

     }); 
    }); 

在第二VIewController我做類似的東西。如果我切換選項卡控制器非常快的應用程序崩潰的

[appDel saveContext]; 

原因很可能是最後一次在viewContext後臺線程用於其他UIviewController

什麼是工作,我周圍可以採用來解決這個問題

如果這是正確實施

[appDel.persistentContainer performBackgroundTask:^(NSManagedObjectContext * _Nonnull context) 
      { 
       NSFetchRequest *request = [Categories fetchRequest]; 
       NSBatchDeleteRequest *deleteReq = [[NSBatchDeleteRequest alloc] initWithFetchRequest:request]; 

       NSError *deleteError = nil; 
       [appDel.persistentContainer.viewContext executeRequest:deleteReq error:&deleteError]; 

       for (int i = 0; i < dataArr.count; i++) 
       { 
        Categories *category = [NSEntityDescription insertNewObjectForEntityForName:@"Categories" inManagedObjectContext:appDel.persistentContainer.viewContext]; 
        [category updateWithDictionary:[dataArr objectAtIndex:i]]; 
       } 

       @try { 

        NSError *error = nil; 
        [context save:(&error)]; 
       } @catch (NSException *exception) 
       { 
       } 

       [self getCategoryItems]; 
      }]; 

回答

1

核心數據是不是線程安全的,既不是閱讀寫作。如果您違反了此核心數據可能會以意想不到的方式失敗。所以,即使它看起來有效,你可以發現核心數據突然崩潰,原因不明。換句話說,訪問來自錯誤線程的核心數據是未定義的。

有幾個可能的解決方案:

1)只使用主線程用於讀取和寫入的核心數據。對於不執行大量數據導入或導出並且數據集相對較小的簡單應用程序,這是一個很好的解決方案。

2)將NSPersistentContainerperformBackgroundTask包裝在操作隊列中,並且只能通過該方法寫入核心數據,並且不會寫入viewContext。當您使用performBackgroundTask時,該方法會爲您提供上下文。您應該使用上下文來獲取您需要的任何對象,修改它們,保存上下文,然後放棄上下文和對象。

如果您嘗試使用performBackgroundTask寫入並直接寫入viewContext,則可能會發生寫入衝突並丟失數據。

+0

我剛纔讀了你的詳細解釋,在另一個問題,幾個問題。當做一個抓取請求讓我們說1000條目,我不應該在後臺線程中做,以避免UI凍結?也可以修改我的代碼作爲示例,應該如何將它放在第一位 –

+0

否。UI不會凍結。提取通常不需要那麼長時間。 1000個條目是一個小數據集。 –

+0

好,謝謝,但是寫作後,我需要調用一個函數如何,我會做到這一點,檢查我的編輯請 –

0

創建在後臺排隊NSPrivateQueueConcurrencyType您的數據處理孩子NSManagedObjectContext對象。

閱讀Concurrency guide獲取更多信息。