在多个线程上访问ViewContext导致崩溃

时间:2017-07-18 08:38:03

标签: ios objective-c core-data

我在标签栏中有两个UIViewController

其中一个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];
            }];

2 个答案:

答案 0 :(得分:2)

核心数据不是线程安全的,也不是用于写入的读取。如果您违反了这一点,核心数据可能会以意想不到的方式失败。因此,即使它看起来有效,你也可以发现核心数据突然崩溃,原因并不明显。换句话说,从错误的线程访问核心数据是不确定的。

有几种可能的解决方案:

1)仅使用主线程来读取和写入核心数据。对于不进行大量数据导入或导出且数据​​集相对较小的简单应用程序,这是一个很好的解决方案。

2)将NSPersistentContainer的{​​{1}}换行到操作队列中,只通过该方法写入核心数据,而不写入performBackgroundTask。当您使用viewContext时,该方法会为您提供上下文。您应该使用上下文来获取所需的任何对象,修改它们,保存上下文,然后丢弃上下文和对象。

如果您尝试使用performBackgroundTask进行编写并直接写入performBackgroundTask,则可能会出现写入冲突并丢失数据。

答案 1 :(得分:0)

使用NSManagedObjectContext在后​​台队列中创建数据处理的子NSPrivateQueueConcurrencyType对象。

阅读Concurrency guide了解详情。