NSFetchedResultsController不总是调用didChangeObject:atIndexPath:forChangeType:newIndexPath:for NSFetchedResultsChangeMove [英] NSFetchedResultsController doesn't always call didChangeObject:atIndexPath:forChangeType:newIndexPath: for NSFetchedResultsChangeMove
问题描述
我在请求中使用 NSFetchedResultsController
和 sortDescriptors
来填充表中有很多结果。我注意到,当发生更改,从一个行从底部附近移动到顶部, didChangeObject:atIndexPath:forChangeType:newIndexPath:
根本不调用。 / p>
奇怪的是,我可以通过遍历所有获取的对象并访问任何属性,直接调用 performFetch
有关问题可能出现的任何提示,或者这只是一个模糊的苹果错误?
这是我的代码:
NSManagedObjectContext * context = [self managedObjectContext];
NSFetchRequest * request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:@MyObjectinManagedObjectContext:context];
request.sortDescriptors = @ [NSSortDescriptor sortDescriptorWithKey:@orderascending:NO]];
request.fetchBatchSize = 20;
NSFetchedResultsController * fetched = [[NSFetchedResultsController alloc]
initWithFetchRequest:request
managedObjectContext:context
sectionNameKeyPath:nil
cacheName:nil];
fetched.delegate = self;
NSError * error = nil;
if(![fetched performFetch:& error]){
NSLog(@未解决的错误提取对象:%@,错误)
}
//不应该是必需的,但是底部附近的对象不会移动到顶部。
for(MyObject * o in fetched.fetchedObjects){
o.someAttribute;
}
2014年9月12日更新:
我将所有数据保存在后台管理的对象上下文中,它似乎与我看到的问题有关。这是我的代码合并更改从主对象上下文:
+(void)initSaveListener {
[[ NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChanges :)
name:NSManagedObjectContextDidSaveNotification
object:[self privateContext]];
}
+(void)mergeChanges:(NSNotification *)notification {
NSManagedObjectContext * context = [self mainContext];
[context performBlock:^ {
[context mergeChangesFromContextDidSaveNotification:notification];
NSError * error = nil;
if(![context save:& error]){
NSLog(@错误合并更改%@,%@,错误,[error userInfo]);
}
}];
}
是由 managedObjectContext
的变化是使用 NSManagedObjectContextDidSaveNotification
从另一个上下文传播的事实造成的。这篇博文详细解释了为什么这会导致 NSFetchedResultsController
的问题,以及如何解决它:
http://www.mlsite.net/blog/?p=518
这里是我上面代码上下文中的具体修复:
+ void)mergeChanges:(NSNotification *)notification {
NSManagedObjectContext * context = [self mainContext];
//断开所有已更改的对象,以便NSFetchedResultsController将看到更改。
NSArray * objects = [[notification userInfo] objectForKey:NSUpdatedObjectsKey];
for(NSManagedObject * object in objects){
[[context objectWithID:[object objectID]] willAccessValueForKey:nil];
}
[context performBlock:^ {
[context mergeChangesFromContextDidSaveNotification:notification];
NSError * error = nil;
if(![context save:& error]){
NSLog(@错误合并更改%@,%@,错误,[error userInfo]);
}
}];
}
I'm using NSFetchedResultsController
with sortDescriptors
on the request to populate a table with a lot of results in it. I notice that when a change occurs that moves a row from near the bottom of the table to the top, didChangeObject:atIndexPath:forChangeType:newIndexPath:
is not called at all.
Strangely, I can work around this by iterating through all the fetched objects and accessing any attribute on them right after calling performFetch
.
Any tips on what the problem might be, or is this just an obscure Apple bug?
Here is my code:
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:@"MyObject" inManagedObjectContext:context];
request.sortDescriptors = @[NSSortDescriptor sortDescriptorWithKey:@"order" ascending:NO]];
request.fetchBatchSize = 20;
NSFetchedResultsController *fetched = [[NSFetchedResultsController alloc]
initWithFetchRequest:request
managedObjectContext:context
sectionNameKeyPath:nil
cacheName:nil];
fetched.delegate = self;
NSError *error = nil;
if (![fetched performFetch:&error]) {
NSLog(@"Unresolved error fetching objects: %@", error);
}
// Should not be necessary, but objects near the bottom won't move to the top without it.
for (MyObject *o in fetched.fetchedObjects) {
o.someAttribute;
}
Updated September 12, 2014:
I'm saving all data in a background managed object context, and it seems to be related to the issues I'm seeing. Here is my code for merging changes from to the main object context:
+(void)initSaveListener {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:[self privateContext]];
}
+(void)mergeChanges:(NSNotification*)notification {
NSManagedObjectContext *context = [self mainContext];
[context performBlock:^{
[context mergeChangesFromContextDidSaveNotification:notification];
NSError *error = nil;
if (![context save:&error]) {
NSLog(@"error merging changes %@, %@", error, [error userInfo]);
}
}];
}
It turns out that this problem was caused by the fact that the changes to managedObjectContext
were being propagated from another context using NSManagedObjectContextDidSaveNotification
. This blog post explains in detail why this causes a problem for NSFetchedResultsController
, and how to fix it:
http://www.mlsite.net/blog/?p=518
Here is the specific fix in the context of my code above:
+(void)mergeChanges:(NSNotification*)notification {
NSManagedObjectContext *context = [self mainContext];
// Fault all objects that have changed so that NSFetchedResultsController will see the changes.
NSArray *objects = [[notification userInfo] objectForKey:NSUpdatedObjectsKey];
for (NSManagedObject *object in objects) {
[[context objectWithID:[object objectID]] willAccessValueForKey:nil];
}
[context performBlock:^{
[context mergeChangesFromContextDidSaveNotification:notification];
NSError *error = nil;
if (![context save:&error]) {
NSLog(@"error merging changes %@, %@", error, [error userInfo]);
}
}];
}
这篇关于NSFetchedResultsController不总是调用didChangeObject:atIndexPath:forChangeType:newIndexPath:for NSFetchedResultsChangeMove的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!