2017-07-18 48 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获取更多信息。