我有一个NSFetchedResultsController
和一些操作通过NSOperationQueue
在不同的线程上更新托管对象。
FRC(及其谓词)如下所示:
- (NSFetchedResultsController*)fetchedResultsController
{
if(fetchedResultsController) return fetchedResultsController;
NSManagedObjectContext* mainContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:@"Check" inManagedObjectContext:mainContext]];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"isSync == %@", [NSNumber numberWithBool:NO]]];
[fetchRequest setFetchBatchSize:10];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:mainContext sectionNameKeyPath:nil cacheName:nil];
fetchedResultsController.delegate = self;
[fetchRequest release], fetchRequest = nil;
return fetchedResultsController;
}
主线程和线程操作具有自己的托管对象上下文。他们只共享同一个协调员。
在线程操作中,我将isSync
属性从NO
更改为YES
。要知道要更新的Check
实体是什么,主要上下文将传递给线程NSManagedObjectID
。
线程操作检索托管对象,如下所示:
-(void)main
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSManagedObjectContext *exportContext = [[NSManagedObjectContext alloc] init];
[exportContext setPersistentStoreCoordinator:[self persistentStoreCoordinator]];
//...
Check* check = (Check*)[exportContext existingObjectWithID:objID error:&error];
check.isSync = [NSNumber numberWithBool:YES];
//...
[exportContext save:&error];
[pool release], pool = nil;
}
当线程操作调用save
时,调用mergeChangesFromContextDidSaveNotification
通知并且主上下文合并更改。
- (void)contextChanged:(NSNotification*)notification
{
if ([notification object] == [self managedObjectContext]) return;
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(contextChanged:) withObject:notification waitUntilDone:YES];
return;
}
[[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}
记录notification
潜在客户的说明,以验证是否正确执行了更改。
我的问题
NSFetchedResultsControllerDelegate
的委托方法未被调用。
这很奇怪,因为处理相同的上下文(主要的上下文)允许监听更改并调用委托方法,例如删除UITableView
中的行对象。
我在SO上发现了一些有同样问题的主题。我已经尝试了所有的解决方法,但我找不到有价值的解决方案:
NSFetchedResultsController not showing updates from other contexts
NSFetchedResultsController not firing delegate method after merging update from background thread
提前谢谢。
修改
上面的代码在以前的模型中工作。然后我创建了一个新模型复制(和粘贴)前一个实体,现在它不再起作用了。
建议?
修改2
这是我在NSFetchedResultsController
getter中使用的谓词。这是我的错,但是当我写这篇文章时,我没有复制它。
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"insertionDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
// previous code here
[fetchRequest setSortDescriptors:sortDescriptors];
现在,关于 Jody 最后评论
在你的NSOperation的main()中,你正在加载新的对象,并在 看起来你正在为每个新对象设置isSync为YES。 您用于fetchedResultsController的谓词只是查找 对于具有isSync == NO。
的对象
我希望当属性isSync
设置为YES时,NSFetchedResultsController
会观察到更改并删除与谓词不匹配的行。我错了吗?
请记住,当将更改从后台合并到主线程时,我能够看到很少有对象更新了它们的isSync
属性。
答案 0 :(得分:11)
你有基本的想法,所以你的代码中可能存在一个错误......
仔细检查您是否正确注册以接收来自后台MOC的通知。
注册以接收来自所有对象的所有通知。在该方法中,记录事件及其所有数据。当对象是MOC时,转储其所有属性(尤其是已注册,插入,更新和删除的对象的列表)。
在保存调用之前和之后放置一条日志语句,并在通知处理程序中合并通知。
此外,您省略了很多代码,因此很难知道您实际在做什么,但是您包含的代码示例看起来很难将所有正在加载的对象的isSync设置为YES,但是您的获取请求只需要那些将isSync设置为NO。这些新对象都不会传递该谓词。
最后,仔细检查您的模型定义并确保使用正确的数字类型。这可能是问题的重要原因。
修改强>
哦是的,我忘了...你的获取请求没有排序描述符。创建FRC时,您的获取请求必须至少包含一个排序描述符...如果您有多个部分,则使用第一个排序描述符将对象分组为多个部分。
跟进Alexsander的评论......我在帖子的开头提到了它,但你肯定不想收听MOC的通知,除非它是众所周知的(当然,除非,您只是为了调试而登录)。你应该知道你正在使用的MOC。
此外,我建议使用父/子MOC进行此类处理,但如果处理得当,您正在做的事情应该有效。
父(私有并发类型) 主要(主要并发类型)
然后,使用您的背景MOC,让他们将主moc设置为其父级。当他们保存时,他们的对象被直接注入主MOC。然后,主MOC可以在以后发出保存以将它们放到磁盘上。
或者,您可以将背景MOC作为父母的父母,然后“主”MOC可以重新发出获取以从父母那里获取数据。
答案 1 :(得分:2)
我遇到了同样的问题,我用父/子上下文解决了这个问题。 这是我遇到的问题。
我正在后台线程中更新我的Core Data对象图,后台线程有自己的managedObjectContext
(这是必需的),而我的fetchedResultsController
无法获取对数据库所做的更改。
解决之后,我做了一些笔记:
ManagedObjectContext
不是线程安全的,这意味着managedObjectContext
无法与其他线程共享。如果一个线程需要使用managedObjectContext
,那么它将初始化它自己的managedObjectContext。
要初始化managedObjectContext
,有两种方法:
alloc/init
然后设置其persistentStoreCoordinator
属性
alloc/init
然后设置parentContext
属性而不是persistentStoreCoordinator
属性
注意:我们无法同时设置persistentStoreCoordinator
的{{1}}和parentContext
属性。
当一个上下文在“链接到控制器和需要仅在主线程上使用的UI对象(核心)的后台线程上运行时,需要使用父/子上下文数据文件)。
以下是父/子上下文所需的要求:
父上下文存在于主线程
子上下文存在于后台线程
这两种情境都需要使用managedObjectContext
方法进行初始化。
在子上下文中完成一批更改后,两个上下文都需要执行保存操作。这些保存操作需要嵌套在performBlock方法中,即:
initWithConcurrencyType:NSMainQueueConcurrencyType
编辑:上面的代码实际上是一个坏主意有两个原因:
1)它可以在不保存父上下文的情况下工作。
2)如果父上下文在其上运行,则主线程被阻塞。
我希望它有所帮助!
编辑:这是一个stackoverflow线程,它帮助了我:Does a Core Data parent ManagedObjectContext need to share a concurrency type with the child context?