更改托pipe对象属性不会触发NSFetchedResultsController更新表视图

我有一个带谓词的fetchedResultsController ,其中“isOpen == YES”

当调用closeCurrentClockSet时 ,我将该属性设置为NO 。 因此,它不应该再出现在我的tableView。

由于某种原因,这没有发生。

有人可以帮我解决这个问题吗?

-(void)closeCurrentClockSet { NSPredicate * predicate = [NSPredicate predicateWithFormat:@"isOpen == YES"]; NSArray *fetchedObjects = [self fetchRequestForEntity:@"ClockSet" withPredicate:predicate inManagedObjectContext:[myAppDelegate managedObjectContext]]; ClockSet *currentClockSet = (ClockSet *)fetchedObjects.lastObject; [currentClockSet setIsOpen:[NSNumber numberWithBool:NO]]; } 

我有更多的方法,使用完全相同的方法 ,通过调用自定义的fetchRequestForEntity:withPredicate:inManagedObjectContext方法。

在这些方法中,当更改属性时,tableView会正确更新! 但是上面这一个( closeCurrentClockSet ),不! 我无法弄清楚为什么。

我的fetchedResultsController的实现来自Apple的文档。

另外,另一个细节。 如果我发送我的应用程序,背景。 closures它,重新打开,tableView显示更新,因为它应该!

我已经尽力在stackoverflow上遵循先前的问题。 没有运气。 我也NSLogged这个骨头。 该对象正在被正确提取。 这是正确的。 isOpen属性正在被正确更新为NO 。 但由于某种原因,我fetchedResultsController不会更新tableView。

我尝试了一些“锤子”解决scheme,像reloadData和调用performFetch。 但是这没有用。 或者是有道理的使用它们…

编辑:从头开始,它是工作,调用reloadData imediatly后performFetch在我的resultsController但使用reloadData锤击解决scheme。 另外,它会取出所有的animation。 我想我的控制器自动更新我的tableView。

有人可以帮我解决这个问题吗?

任何帮助是极大的赞赏!

谢谢,

努诺

编辑:

完整的实现。

fetchedResultsController是非常标准和直接的。 其他的一切都来自Apple的文档

 - (NSFetchedResultsController *)fetchedResultsController { if (_fetchedResultsController) { return _fetchedResultsController; } NSManagedObjectContext * managedObjectContext = [myAppDelegate managedObjectContext]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"ClockPair" inManagedObjectContext:managedObjectContext]; NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; [fetchRequest setEntity:entity]; NSString *predicate = [NSString stringWithFormat: @"clockSet.isOpen == YES"]; [fetchRequest setPredicate: [NSPredicate predicateWithFormat:predicate]]; NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:@"clockIn" ascending:NO]; NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, nil]; [fetchRequest setSortDescriptors:sortDescriptors]; [fetchRequest setFetchBatchSize:20]; NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:@"Root"]; _fetchedResultsController = theFetchedResultsController; _fetchedResultsController.delegate = self; return _fetchedResultsController; } 

苹果公司文档的样板代码:

 - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { // The fetch controller is about to start sending change notifications, so prepare the table view for updates. [self.tableView beginUpdates]; } - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { UITableView *tableView = self.tableView; switch(type) { case NSFetchedResultsChangeInsert: [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationTop]; break; case NSFetchedResultsChangeDelete: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeUpdate: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeMove: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft]; [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationTop]; break; } } - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { UITableView *tableView = self.tableView; switch(type) { case NSFetchedResultsChangeInsert: [tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; } } - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { // The fetch controller has sent all current change notifications, so tell the table view to process all updates. [self.tableView endUpdates]; } 

第一次更新:

跟踪[managedObjectContext hasChanges]不会返回YES,因为它应该。 但fetchedResultsController不会更新tableView

2ND更新

didChangeObject:atIndexPath: 不会被调用这个特殊情况! 我有两个更多的方法,与EXACT相同的代码,他们恰好是一个不同的实体。 他们完美地工作。 谢谢@Leonardo指出这一点

第三个更新这个方法,遵循相同的规则。 但实际上工作。

 - (void)clockOut { NSPredicate * predicate = [NSPredicate predicateWithFormat:@"isOpen == %@", [NSNumber numberWithBool:YES]]; NSArray * fetchedObjects = [self fetchRequestForEntity:@"ClockPair" withPredicate:predicate inManagedObjectContext:[myAppDelegate managedObjectContext]]; ClockPair *aClockPair = (ClockPair *)fetchedObjects.lastObject; aClockPair.clockOut = [NSDate date]; aClockPair.isOpen = [NSNumber numberWithBool:NO]; } 

任何人有任何其他的想法,我可能会失踪?

谢谢,

努诺

好的,我会解释你的问题,然后我会让你判断它是否是FRC中的一个bug。 如果你认为这是一个错误,那么你真的应该用苹果提交一个错误报告。

您的读取结果控制器谓词是这样的:

 NSString *predicate = [NSString stringWithFormat: @"clockSet.isOpen == YES"]; 

这是一个布尔值的有效谓词。 它将遵循clockSet实体的关系并获取它的isOpen属性。 如果是YES那么这些对象将被接受到对象数组中。

我觉得我们很好。

现在,如果将clockSet.isOpen属性之一更改为NO ,那么您希望看到该对象从您的表视图中消失(即,它不应再与谓词相匹配,因此应将其从获取对象的数组中移除)。

所以,如果你有这个…

 [currentClockSet setIsOpen:[NSNumber numberWithBool:NO]]; 

那么,无论哪个顶级对象与currentClockSet有关系,都应该从FRC获取结果数组中“消失”。

但是,你不会看到它消失。 原因是由FRC监视的对象没有改变。 是的,谓词键path改变了,但是FRC持有ClockSet实体和一个实际改变的ClockSet实体。

你可以看到通知四处飞转,看看幕后发生了什么。

无论如何,当您执行提取操作时,FRC将使用键path,但不会监视不在其实际获取对象集合中的对象的更改。

最简单的解决方法是为保存此关键path对象的对象“设置”一个属性。

例如,我注意到ClockPair也有一个isOpen属性。 如果你有一个相反的关系,那么你可以这样做…

 currentClockSet.isOpen = NO; currentClockSet.clockPair.isOpen = currentClockSet.clockPair.isOpen; 

注意你根本没有改变这个值。 但是,调用者被调用,从而触发KVO,并因此触发了私人的DidChange通知,然后告知FRC该对象已更改。 因此,它重新评估检查,看看是否应该包含对象,find改变的keypath值,并做你的期望。

因此,如果在FRC谓词中使用关键path,那么如果更改该值,则需要回到FRCarrays中的所有对象并将其“弄脏”,以便这些对象位于通知中传递了有关对象的变化。 这很丑陋,但可能比保存或更改提取请求和重新获取更好。

我知道你不相信我,所以继续尝试吧。 请注意,要使其工作,您必须知道FRCarrays中的哪些项目会受到更改的影响,并“戳”它们以使FRC注意到更改。

如前所述,另一个选项是保存上下文,并重新获取值。 如果您不想保存上下文,则可以在当前上下文中执行获取包含更新,而无需从商店刷新。

我发现伪造FRC正在观察的对象的变化是完成对作为其他实体的关键path的谓词的重新评估的最佳方式。

好的,所以,这是否是一个错误是有争议的。 我个人认为,如果FRC要监控一个关键path,它应该一直这样做,而不是像我们在这里看到的那样。

我希望这是有道理的,我鼓励你提交一个错误报告。

[currentClockSet setIsOpen:[NSNumber numberWithBool:NO]]; 你可以保存pipe理的对象上下文:

 NSError *saveError = nil; if( ![[myAppDelegate managedObjectContext] save:&saveError] ) { // handle error saving context } 

我怀疑你的UITableView将在保存上下文后正确更新。 这可能是为什么发送您的应用程序的背景作品。 我怀疑你的核心数据堆栈是在应用程序的委托中设置的,当它进入后台时,它会在主NSManagedObjectContext上执行一个保存。

你遇到了类似的问题。

我知道这个问题很老,但我希望这可以帮助别人:

最简单的方法是在父对象中引入名为lastUpdated: NSDate的新属性。

我有一个Conversation ,其中包含几个Messages 。 每当消息的isRead标志更新时,我只需要在ConversationOverviewViewController中显示一个只显示Conversation的更新。 此外, ConversationOverviewVCNSFetchedResultsController只能获取Conversation ,并不知道任何有关Message

每当消息更新时,我调用message.parentConversation.lastUpdated = NSDate() 。 这是手动触发更新的简单而有用的方法。

希望这可以帮助。