NSFetchedResultsController 和插入记录的问题
好吧,这让我发疯,我已经尝试了在互联网上可以找到的所有可能的解决方案,但仍然一无所获。所以这是我的情况: 我有一个视图,上面有一个按钮。当触摸它时,它会弹出一个客户列表供用户选择。当他们选择它时,我使用 fetchedresultscontroller 来获取与客户关联的部件并将它们显示在表格视图中。这一切都运作良好。问题是,如果我选择了客户 A 并插入一个新部件,然后转到客户 B 并插入一个新部件,当我重新选择客户 A 并尝试插入另一个部件时,应用程序崩溃并出现以下错误:
* -[UITableView _endCellAnimationsWithContext:]、/SourceCache/UIKit_Sim/UIKit-1447.6.4/UITableView.m:976 中断言失败 2011-02-21 10:39:12.896 SalesPro[36203:207] 严重 应用程序错误。一个例外是 从代表处抓获 NSFetchedResultsController 期间 调用 -controllerDidChangeContent:。 无效更新:行数无效 在第 0 节中。行数 包含在现有部分之后 更新 (1) 必须等于 其中包含的行数 更新之前的部分 (1),加上或 减去插入的行数或 从该部分删除(插入 1 个, 0 已删除)。与用户信息(空) 2011-02-21 10:39:12.907 SalesPro[36203:207] * 终止 应用程序由于未捕获的异常 'NSRangeException',原因: '-[UITableView scrollToRowAtIndexPath:atScrollPosition:animated:]: 第 (1) 行超出部分边界 (1) (0).'
处理客户选择的代码
-(void) CustomerSelectedRaised:(NSNotification *)notif
{
NSLog(@"Received Notification - Customer Selected");
selectedCustomer = (Customer *)[notif object];
[self buildCustomerInfoText];
if (popoverController != nil) {
[popoverController dismissPopoverAnimated:YES];
}
fetchedResultsController = nil;
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
//Update to handle error appropriately
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
exit(-1); //fail
}
[self.partsListGrid reloadData];
}
FetchedResultsController代码
#pragma mark -
#pragma mark Fetch results controller
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
//set-up fetched results controller
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"PartsList" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSLog(@"TAMS ID: %@", selectedCustomer.customerTAMSID);
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"customerTAMSID == %@", selectedCustomer.customerTAMSID]];
//set to sort by customer name
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"sortOrder" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:[self managedObjectContext]
sectionNameKeyPath:nil cacheName:nil];
[aFetchedResultsController setDelegate:self];
[self setFetchedResultsController:aFetchedResultsController];
//clean-up
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
//return results
return fetchedResultsController;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController*)controller
{
[[self partsListGrid] beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// In the simplest, most efficient, case, reload the table view.
[[self partsListGrid] endUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.partsListGrid;
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 withHeight:tableView.rowHeight];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.partsListGrid insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.partsListGrid deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeMove:
break;
case NSFetchedResultsChangeUpdate:
break;
default:
break;
}
}
添加零件代码
-(void) addScannedPart:(Part *)part
{
// Check to see if entered part is already in list
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *partsEntity = [NSEntityDescription entityForName:@"PartsList" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:partsEntity];
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"customerTAMSID == %@ AND lineAbbreviation == %@ AND partNumber == %@", selectedCustomer.customerTAMSID, part.lineAbbrev, part.partNumber];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedParts = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if ([fetchedParts count] == 0) {
//Create a new instance of the entity managed object by the fetched results controller
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[fetchedResultsController fetchRequest] entity];
NSLog(@"Entity Name: %@", [entity name]);
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
//Add fields to Managed Object
int sortOrder = [[fetchedResultsController fetchedObjects] count];
sortOrder++;
[newManagedObject setValue:[part lineAbbrev] forKey:@"lineAbbreviation"];
[newManagedObject setValue:[part partNumber] forKey:@"partNumber"];
[newManagedObject setValue:[NSNumber numberWithInt:[[part orderQty] intValue]] forKey:@"orderQuantity"];
[newManagedObject setValue:selectedCustomer.customerTAMSID forKey:@"customerTAMSID"];
[newManagedObject setValue:[NSNumber numberWithInt:sortOrder] forKey:@"sortOrder"];
//Save the context
NSError *error = nil;
if (![context save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
//reload Customer list
NSIndexPath *insertionPath = [fetchedResultsController indexPathForObject:newManagedObject];
[self.partsListGrid selectRowAtIndexPath:insertionPath animated:YES scrollPosition:UITableViewScrollPositionTop];
[self.partsListGrid reloadData];
}
}
这是我必须为下一个版本找出的最大(最严重)缺陷(很快)。我将不胜感激任何和所有的帮助!谢谢!
Ok, this one is driving me crazy and I've tried everything I can find on the internet as a possible solution but still nothing. So here is my situation:
I have a view that has a button on it. When this is touched it pops up a list of customers for the user to select from. when they select it, i make a use the fetchedresultscontroller to get the parts associated to the customer and display them in a tableview. This all works well. the problem is if I have Customer A selected and insert a new part, then go to Customer B and insert a new part, when i reselect Customer A and try to insert another part the app crashes with the following error:
* Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-1447.6.4/UITableView.m:976
2011-02-21 10:39:12.896
SalesPro[36203:207] Serious
application error. An exception was
caught from the delegate of
NSFetchedResultsController during a
call to -controllerDidChangeContent:.
Invalid update: invalid number of rows
in section 0. The number of rows
contained in an existing section after
the update (1) must be equal to the
number of rows contained in that
section before the update (1), plus or
minus the number of rows inserted or
deleted from that section (1 inserted,
0 deleted). with userInfo (null)
2011-02-21 10:39:12.907
SalesPro[36203:207] * Terminating
app due to uncaught exception
'NSRangeException', reason:
'-[UITableView
scrollToRowAtIndexPath:atScrollPosition:animated:]:
row (1) beyond bounds (1) for section
(0).'
Code that handles selecting of Customer
-(void) CustomerSelectedRaised:(NSNotification *)notif
{
NSLog(@"Received Notification - Customer Selected");
selectedCustomer = (Customer *)[notif object];
[self buildCustomerInfoText];
if (popoverController != nil) {
[popoverController dismissPopoverAnimated:YES];
}
fetchedResultsController = nil;
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
//Update to handle error appropriately
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
exit(-1); //fail
}
[self.partsListGrid reloadData];
}
FetchedResultsController code
#pragma mark -
#pragma mark Fetch results controller
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
//set-up fetched results controller
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"PartsList" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSLog(@"TAMS ID: %@", selectedCustomer.customerTAMSID);
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"customerTAMSID == %@", selectedCustomer.customerTAMSID]];
//set to sort by customer name
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"sortOrder" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:[self managedObjectContext]
sectionNameKeyPath:nil cacheName:nil];
[aFetchedResultsController setDelegate:self];
[self setFetchedResultsController:aFetchedResultsController];
//clean-up
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
//return results
return fetchedResultsController;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController*)controller
{
[[self partsListGrid] beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// In the simplest, most efficient, case, reload the table view.
[[self partsListGrid] endUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.partsListGrid;
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 withHeight:tableView.rowHeight];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.partsListGrid insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.partsListGrid deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeMove:
break;
case NSFetchedResultsChangeUpdate:
break;
default:
break;
}
}
Add Part code
-(void) addScannedPart:(Part *)part
{
// Check to see if entered part is already in list
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *partsEntity = [NSEntityDescription entityForName:@"PartsList" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:partsEntity];
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"customerTAMSID == %@ AND lineAbbreviation == %@ AND partNumber == %@", selectedCustomer.customerTAMSID, part.lineAbbrev, part.partNumber];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedParts = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if ([fetchedParts count] == 0) {
//Create a new instance of the entity managed object by the fetched results controller
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[fetchedResultsController fetchRequest] entity];
NSLog(@"Entity Name: %@", [entity name]);
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
//Add fields to Managed Object
int sortOrder = [[fetchedResultsController fetchedObjects] count];
sortOrder++;
[newManagedObject setValue:[part lineAbbrev] forKey:@"lineAbbreviation"];
[newManagedObject setValue:[part partNumber] forKey:@"partNumber"];
[newManagedObject setValue:[NSNumber numberWithInt:[[part orderQty] intValue]] forKey:@"orderQuantity"];
[newManagedObject setValue:selectedCustomer.customerTAMSID forKey:@"customerTAMSID"];
[newManagedObject setValue:[NSNumber numberWithInt:sortOrder] forKey:@"sortOrder"];
//Save the context
NSError *error = nil;
if (![context save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
//reload Customer list
NSIndexPath *insertionPath = [fetchedResultsController indexPathForObject:newManagedObject];
[self.partsListGrid selectRowAtIndexPath:insertionPath animated:YES scrollPosition:UITableViewScrollPositionTop];
[self.partsListGrid reloadData];
}
}
This is the biggest (most severe) defect I have to figure out for our next release (which is very soon). I'd appreciate any and all help! Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
首先修复泄漏的 NSFetchedResultsCOntroller ,因为
您不应该有多个 NSFetchedResultsController ,它们都报告对同一表视图的更改。我认为这就是正在发生的事情。除非 NSFRController 被释放,否则它将向其委托报告更改
start with fixing the leaking NSFetchedResultsCOntroller in
you shouldn't have multiple NSFetchedResultsControllers that all report changes to the same tableview. And I think this is what is happening. Unless the NSFRController gets deallocated it will report changes to its delegate
好的,多亏了这个 SO 线程,看起来就像我让它工作一样:
我通过 CustomerSelectedRaished 方法进行编辑,如下所示:
ok, thanks to this SO thread, it looks like I have it working:
I edited by CustomerSelectedRaised method to look like so: