刷新和删除后台线程中的内容错误(NSFetchedResultsController)

发布于 2024-09-15 19:43:32 字数 5272 浏览 3 评论 0原文

我正在使用 NSFetchedResultsController 来填充 UITableViewController 的内容。

我正在使用 NSOperation 从 webService 收集数据(我正在使用单独的 ManagedObjectContext,因为它是另一个线程)
保存数据时,我的 ViewController (即 NSFetchedResultsControllerDelegate)被调用,并且我使用 mergeChangesFromContextDidSaveNotification 合并我的 MOC

#pragma mark -
#pragma mark Parsers delegate

- (void)parserWillSave:(id)parser{
    TopNewsParser *emp = (TopNewsParser *)parser;
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(parserContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:emp.managedObjectContext];
    [NSFetchedResultsController deleteCacheWithName:@"aLaUne"];
}

- (void)parserDidSave:(id)parser{
    TopNewsParser *emp = (TopNewsParser *)parser;
    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:emp.managedObjectContext];
}

/**
 Notification from the add controller's context's save operation. This is used to update the fetched results controller's managed object context with the new book instead of performing a fetch (which would be a much more computationally expensive operation).
 */
- (void)parserContextDidSave:(NSNotification*)saveNotification {
    DLog(@"");
    NSManagedObjectContext *fetchContext = [_fetchedResultsController managedObjectContext];
    // Merging changes causes the fetched results controller to update its results
    [fetchContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
                                  withObject:saveNotification
                               waitUntilDone:YES]; 


}

对于 NSFetchedResultsControllerDelegate 我正在使用 CoreData Books 示例中的代码

#pragma mark -
#pragma mark NSFetchedResultsControllerDelegate

- (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 {
    //ALog(@"indexPath: %@ newIndexPath : %@ | type : %d # %@",indexPath,newIndexPath,type,anObject);
    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];
            // Reloading the section inserts a new row and ensures that titles are updated appropriately.
            [tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }

}


- (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)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    UIBarButtonItem *reloadButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh 
                                                                                  target:self                               
                                                                                  action:@selector(refreshTableViewContent)];
    reloadButton.accessibilityLabel = @"Reload";
    self.navigationItem.leftBarButtonItem = reloadButton;

    // The fetch controller has sent all current change notifications, so tell the table view to process all updates.
    [self.tableView endUpdates];

}

我的问题是加载新内容时某些对象所在的位置删除了,我的 tableView 乱了!即使我的 countOfRow 减少 1,该行仍然可见:

然后,当我向下滚动时,我的 tableview 为空(只有 4 个可见行)仍在 tableview 中,否则它是一个空白的滚动视图

在控制台中我可以看到以下消息。

严重的应用程序错误。在调用 -controllerDidChangeContent: 期间,从 NSFetchedResultsController 的委托捕获了异常。 *** -[NSMutableArray removeObjectAtIndex:]: 索引 0 超出带有 userInfo (null) 的空数组的范围

一开始我认为这是由于我的 NSFetchedResultsController 的缓存造成的,但即使我禁用它,我也遇到同样的问题。

有人知道如何解决这个问题吗?

I'm using an NSFetchedResultsController to fill content for my UITableViewController.

I'm using an NSOperation to gather data from a webService (I'm using a separated ManagedObjectContext as it's another thread)
When the data is saved my ViewController (wich is the NSFetchedResultsControllerDelegate) is called and i merge my MOCs using mergeChangesFromContextDidSaveNotification

#pragma mark -
#pragma mark Parsers delegate

- (void)parserWillSave:(id)parser{
    TopNewsParser *emp = (TopNewsParser *)parser;
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(parserContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:emp.managedObjectContext];
    [NSFetchedResultsController deleteCacheWithName:@"aLaUne"];
}

- (void)parserDidSave:(id)parser{
    TopNewsParser *emp = (TopNewsParser *)parser;
    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:emp.managedObjectContext];
}

/**
 Notification from the add controller's context's save operation. This is used to update the fetched results controller's managed object context with the new book instead of performing a fetch (which would be a much more computationally expensive operation).
 */
- (void)parserContextDidSave:(NSNotification*)saveNotification {
    DLog(@"");
    NSManagedObjectContext *fetchContext = [_fetchedResultsController managedObjectContext];
    // Merging changes causes the fetched results controller to update its results
    [fetchContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
                                  withObject:saveNotification
                               waitUntilDone:YES]; 


}

For the NSFetchedResultsControllerDelegate i'm using the code from the CoreData Books sample

#pragma mark -
#pragma mark NSFetchedResultsControllerDelegate

- (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 {
    //ALog(@"indexPath: %@ newIndexPath : %@ | type : %d # %@",indexPath,newIndexPath,type,anObject);
    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];
            // Reloading the section inserts a new row and ensures that titles are updated appropriately.
            [tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }

}


- (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)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    UIBarButtonItem *reloadButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh 
                                                                                  target:self                               
                                                                                  action:@selector(refreshTableViewContent)];
    reloadButton.accessibilityLabel = @"Reload";
    self.navigationItem.leftBarButtonItem = reloadButton;

    // The fetch controller has sent all current change notifications, so tell the table view to process all updates.
    [self.tableView endUpdates];

}

My problem is when load new content where some object where deleted, my tableView is messed up ! The row is still visible even if my countOfRow is reduced by 1 :

Then when i scroll down my tableview is empty (only the 4 visible rows) are still in the tableview otherwise it's a blank scrollView

In the console i can see the following message.

Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. *** -[NSMutableArray removeObjectAtIndex:]: index 0 beyond bounds for empty array with userInfo (null)

In the beggining i tought this was due to the cache of my NSFetchedResultsController but even when i disable it i have the same problem.

Does someone have an idea on how to fix this ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

晨曦慕雪 2024-09-22 19:43:33

这里同样的问题。

人们建议使用 TopSongs 示例代码。修改后的版本在这里:
http:// www.bigbluebrains.com/index.php/2010/08/16/iphone-topsongs-sample-code-memory-leak-fix/

我想知道我们是否应该在共享数据上使用互斥锁。

Same problem here.

People have suggested using TopSongs sample code. Modified version is here:
http://www.bigbluebrains.com/index.php/2010/08/16/iphone-topsongs-sample-code-memory-leak-fix/

I wonder if we should use mutex on the shared data.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文