我被我的第一个 GCD 和使用应用程序的第一个核心数据困住了 =)
两个 View 访问相同的数据(由单个 DAO 处理)。
如果我等待当前 View 完成加载其内容,则更改 View 时不会出现问题。
但是:如果我在一个 Controller 尝试从我的模型中获取数据时更改 View (基于选项卡),则新 Controller 会尝试相同的操作,并且线程会发生“冲突”,并且我的应用程序会卡住。
卡住发生在我的 DAO 的这行代码中:
NSArray *results = [managedObjectContext executeFetchRequest:fetch error:&error];
reloadAllMonth()
访问我的 DAO 的获取例程
我如何在第一个 Controller 中加载数据:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
[self reloadAllMonth];
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self.allMonthTable reloadData];
});
在第二个 View Controller 中,我做的第一件事是更新我的 DAO,这当然使用(在其他 View Controller 之下)与我之前调用的完全相同的获取例程:
[self.dataHandler updateData];
到目前为止我已经尝试了两种方法:
首先使用 c 信号量:
-(NSArray *)fetchAllMonthExpenses{
//@return: array of all expenses in month (day & month type)
NSNumber *monthNumber = [self getMonthNumber:[NSDate date]];
NSEntityDescription *exp = [NSEntityDescription entityForName:@"Expense" inManagedObjectContext:managedObjectContext];
NSFetchRequest *fetch = [[NSFetchRequest alloc]init];
[fetch setEntity:exp];
[fetch setPredicate:[NSPredicate predicateWithFormat:@"month == %@",monthNumber]];
NSError *error = nil;
sem_wait(&isLoading);
NSArray *results = [self.managedObjectContext executeFetchRequest:fetch error:&error];
sem_post(&isLoading);
return results;
}
第二次使用同步指令
-(NSArray *)fetchAllMonthExpenses{
//@return: array of all expenses in month (day & month type)
NSNumber *monthNumber = [self getMonthNumber:[NSDate date]];
NSEntityDescription *exp = [NSEntityDescription entityForName:@"Expense" inManagedObjectContext:managedObjectContext];
NSFetchRequest *fetch = [[NSFetchRequest alloc]init];
[fetch setEntity:exp];
[fetch setPredicate:[NSPredicate predicateWithFormat:@"month == %@",monthNumber]];
NSError *error = nil;
@synchronized(self.class){
NSArray *results = [self.managedObjectContext executeFetchRequest:fetch error:&error];
return results;
}
}
遗憾的是,这两种方法都不起作用,无论我做什么,应用程序都会卡住。
所以我的问题是:我做错了什么(正如我第一次使用线程提到的),我错过了什么,我应该看哪里?
这已经让我忙了两天了,我似乎无法理解它:/
最佳答案
NSManagedObjectContext
及其内部的所有 NSManagedObjects
都不是线程安全的。
无论您使用什么线程来创建上下文,它都必须是您执行与该上下文相关的任何操作的唯一线程。即使只是从一个托管对象读取值也必须在该线程上完成,而不是在任何其他线程上完成。
如果您需要两个处理同一个数据库的线程,您有两个选择:
- 使用
dispatch_sync()
立即跳转到另一个线程,以对托管对象和/或上下文执行所有读/写操作
或者:
- 在另一个线程中为同一数据库创建第二个
NSManagedObjectContext
,并保持对两个上下文所做的任何更改同步。
第一个选项要容易得多,但可能会消除线程的许多好处。第二种选择比较困难,但可以做到,并且有一个相当好的 API 可以使不同线程上的两个上下文保持同步。
查找核心数据编程指南以获取更多详细信息。
关于objective-c - 获取例程中出现死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9889475/