NSFetchedResultsController 忽略 fetchLimit?
我有一个 NSFetchedResultsController 来使用核心数据中的内容更新 UITableView 。这是非常标准的东西,我相信你们都见过很多次,但是我遇到了一些小问题。首先是我的代码:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Article" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchLimit:20];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(folder.hidden == NO)"];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sort1 = [NSSortDescriptor sortDescriptorWithKey:@"sortDate" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sort1, nil]];
NSFetchedResultsController *controller = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
[fetchRequest release];
controller.delegate = self;
self.fetchedResultsController = controller;
[controller release];
NSError *error = nil;
[self.fetchedResultsController performFetch:&error];
if (error) {
// TODO send error notification
NSLog(@"%@", [error localizedDescription]);
}
问题是商店最初没有实体,因为它从网络服务下载和同步。所发生的情况是,NSFetchedResultsController 用来自商店的 150 多行实体填充表,这就是 Web 服务返回的数量。但我将获取限制设置为 20,但它似乎忽略了这一限制。但是,如果我关闭应用程序并使用商店中已有的数据重新开始,它就可以正常工作。我是我的代表,我这样做:
#pragma mark -
#pragma mark NSFetchedResultsControllerDelegate methods
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (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:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
这几乎是从苹果的开发文档中复制粘贴的,你知道发生了什么吗?
I have a NSFetchedResultsController to update a UITableView with content from Core Data. It's pretty standard stuff I'm sure you've all seen many times however I am running into slight problem. First here's my code:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Article" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchLimit:20];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(folder.hidden == NO)"];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sort1 = [NSSortDescriptor sortDescriptorWithKey:@"sortDate" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sort1, nil]];
NSFetchedResultsController *controller = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
[fetchRequest release];
controller.delegate = self;
self.fetchedResultsController = controller;
[controller release];
NSError *error = nil;
[self.fetchedResultsController performFetch:&error];
if (error) {
// TODO send error notification
NSLog(@"%@", [error localizedDescription]);
}
The problem is that initially the store has no entities as it downloads and syncs from a webservice. What happens is that the NSFetchedResultsController fills the table with over 150 rows of entities from the store, which is how many the webservice returns. But I am setting a fetch limit of 20 which it appears to be ignoring. However, if I close out the app and start again with data already in the store, it works fine. Im my delegate i do this:
#pragma mark -
#pragma mark NSFetchedResultsControllerDelegate methods
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (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:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
Which is pretty much copy-paste from Apple's dev documents, any ideas what's goin on?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我知道这是一个老问题,但我有一个解决方案:
由于
NSFetchedResultsController
中存在一个已知错误,它不遵守的
,您必须手动处理fetchlimit
NSFetchRequestUITableViewDataSource
和NSFetchedResultsControllerDelegate
方法中的记录限制。tableView:numberOfRowsInSection:
控制器:didChangeObject:atIndexPath:forChangeType:newIndexPath:
I know this is an old question, but I have a solution for it:
Since there is a known bug in
NSFetchedResultsController
that doesn't honor thefetchlimit
of theNSFetchRequest
, you have to manually handle the limiting of records within yourUITableViewDataSource
andNSFetchedResultsControllerDelegate
methods.tableView:numberOfRowsInSection:
controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:
这是一个老问题,但我自己刚刚遇到过它(在 iOS 5 中)。我认为您遇到了此处描述的错误: https://devforums.apple.com/消息/279576#279576。
该线程根据您是否有sectionNameKeyPath 提供解决方案。因为我(像你一样)没有这样做,所以答案是将 tableview 与 fetchedResultsController 分离。例如,不要使用它来确定行数:
只需返回您期望的内容:
在
controller:didChangeObject
中,仅当 newIndexPath 在您的 fetchLimit 范围内时才插入新对象。This is an old question but I just ran into it myself (in iOS 5). I think you're running into the bug described here: https://devforums.apple.com/message/279576#279576.
That thread provides solutions based on whether you have a sectionNameKeyPath or not. Since I (like you) didn't, the answer is to decouple the tableview from the fetchedResultsController. For example, instead of using it to determine the number of rows:
just return what you expect:
And in
controller:didChangeObject
, only insert the new object if the newIndexPath is within your fetchLimit.在某些情况下,这些仍然会崩溃,例如多次插入,或超出限制,...
您必须将所有更改保存到 4 组,并计算另外 4 个数组,并在
-[UITableView endUpdates]
之前删除/更新/插入到 tableView类似的事情(假设只有一个部分):
These will still crash in some situations, like several inserts, or move over limit,...
You have to save all the changes to 4 sets, and calculate another 4 arrays and delete/update/insert to tableView before
-[UITableView endUpdates]
Some thing like (assume there is only one section):
您遇到的问题是,您在加载 fetchedResultsController 之前调用了完整的数据,因此它会向您显示您需要做的一切就是加载所有信息,然后调用 fetchedResultsController
示例
The problem you have is that you are calling before loading fetchedResultsController charge the full data so it shows you everything you need to do is load all the information and then call fetchedResultsController
Example
来自苹果文档: https://developer.apple.com/reference/coredata/nsfetchrequest /1506622-fetchlimit
From apple doc: https://developer.apple.com/reference/coredata/nsfetchrequest/1506622-fetchlimit
早在 2014 年,我就 iOS 6/7 向 Apple 提交了关于此问题的错误报告。正如许多其他人所指出的,这仍然是 iOS 9 和 10 上的一个错误。我最初的错误报告也仍然开放,没有来自苹果的反馈。 这是该错误报告的 OpenRadar 副本。
这是我已经成功使用的修复程序,但它会得到多次调用。谨慎使用。
I filed a bug report with Apple back in 2014 on iOS 6/7 about this issue. As many others have noted, it's still a bug on iOS 9 and 10. My original bug report is still open too with no feedback from Apple. Here is an OpenRadar copy of that bug report.
Here's a fix I've used with success but it will get called multiple times. Use with caution.
这是我的技巧:
在调用 NSManagedObjectContext 实例上的“save”方法后,我设置了 NSFetchedResultsController 的委托。
,发布具有该名称的通知:“同步”并触发一个设置委托ps 的函数(在视图控制器中)。如果您不再需要该观察者,请记得将其删除
This is my trick:
I set the NSFetchedResultsController's delegate after 'save' method on the NSManagedObjectContext instance is called.
ps. remember to remove that observer if you don't need it anymore