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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在多个线程上访问ViewContext导致崩溃相关的知识,希望对你有一定的参考价值。

我在Tab栏中有两个UIViewControllers

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

2)在一个操作队列中包装NSPersistentContainerperformBackgroundTask,并且只通过该方法写入核心数据,并且永远不会写入viewContext。当您使用performBackgroundTask时,该方法为您提供上下文。您应该使用上下文来获取所需的任何对象,修改它们,保存上下文,然后丢弃上下文和对象。

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

另一答案

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

阅读Concurrency guide了解更多信息。

以上是关于在多个线程上访问ViewContext导致崩溃的主要内容,如果未能解决你的问题,请参考以下文章

多线程 Win32 C++ 程序在多个线程中使用 try/catch 崩溃

C++ 无锁队列与多线程崩溃

同时调用多个 ViewController 实例会导致应用在 AppDelegate 中崩溃

通过多个线程访问向量?

当无法在多个线程上连接到服务器时,pymssql 的段错误

RT-Thread多线程导致的临界区问题