gpt4 book ai didi

objective-c - 更改托管对象属性不会触发 NSFetchedResultsController 更新 TableView

转载 作者:IT王子 更新时间:2023-10-29 07:56:43 25 4
gpt4 key购买 nike

我有一个带有谓词的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 的文档。

还有一个细节。如果我将我的应用程序发送到后台。关闭它并重新打开,tableView 显示已更新!

我已尽力在 stackOverflow 上关注之前的问题。没运气。我也 NSLogged 到骨头。正在正确获取对象。这是正确的。 isOpen 属性 已正确更新为NO。但出于某种原因,我的 fetchedResultsController 没有更新 tableView。

我确实尝试了几个“锤子”解决方案,例如 reloadData 和调用 performFetch。但这没有用。或者使用它们是有意义的......

编辑:从头开始,它确实有效,在我的 resultsController 上执行 performFetch 后立即调用 reloadData,但使用 reloadData 正在敲定一个解决方案。另外,它会删除所有动画。我希望我的 Controller 自动更新我的 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;

}

--

Apple 文档中的样板代码:

- (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];
}

第一次更新:

Tracking [managedObjectContext hasChanges] 确实返回 YES,这是应该的。但是 fetchedResultsController 没有更新 tableView

第二次更新

didChangeObject:atIndexPath: 没有被调用 对于这种特殊情况!我还有 2 个方法,使用完全相同的代码,它们只是碰巧是不同的实体。他们工作得很好。感谢@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];


}

有人对我可能遗漏的内容有任何其他想法吗?

谢谢,

努诺

最佳答案

OK,我会解释你的问题,然后让你判断是不是FRC的bug。如果您认为这是一个错误,那么您真的应该向 Apple 提交错误报告。

你的获取结果 Controller 谓词是这样的:

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

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

我认为我们到此为止都很好。

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

所以,如果你有这个......

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

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

但是,您并没有看到它消失。原因是FRC监控的对象没有变化。是的,谓词键路径发生了变化,但 FRC 包含实体 ClockPairClockSet 实体实际上发生了变化。

您可以观看通知飞来飞去,看看幕后发生了什么。

无论如何,FRC 将在您进行抓取时使用关键路径,但它不会监视对不在其实际抓取对象集中的对象的更改。

最简单的解决方法是为包含此关键路径对象的对象“设置”一个属性。

例如,我注意到 ClockPair 也有一个 isOpen 属性。如果你有逆向关系,那么你可以这样做......

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

请注意,您实际上根本没有更改该值。然而,setter 被调用,这触发了 KVO,因此私有(private)的 DidChange 通知,然后告诉 FRC 对象改变了。因此,它会重新评估检查以查看是否应包含该对象,发现 keypath 值已更改,然后执行您期望的操作。

因此,如果您在 FRC 谓词中使用关键路径,如果您更改该值,则需要蠕动回到 FRC 数组中的所有对象并“弄脏它们”,以便这些对象位于传递有关对象更改的通知。这很丑陋,但可能比保存或更改您的提取请求并重新提取要好。

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

如前所述,另一种选择是保存上下文并重新获取值。如果您不想保存上下文,则可以使获取包含当前上下文中的更新,而无需从存储中刷新。

我发现,对 FRC 正在监视的对象进行伪造更改是完成对作为其他实体的关键路径的谓词进行重新评估的最佳方式。

好的,所以,这是否是错误还有待商榷。就我个人而言,我认为如果 FRC 要监控 key 路径,它应该一直监控,而不是像我们在这里看到的那样部分监控。

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

关于objective-c - 更改托管对象属性不会触发 NSFetchedResultsController 更新 TableView ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12370521/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com