1

我无法弄清楚这一点,但是当我从NSFetchedResultsController.

当我删除我的对象时,我这样做:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete the object
        NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
        [context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];

        [self saveContext];
    }
}

设置NSFetchedResultsController

- (NSFetchedResultsController *)fetchedResultsController {
    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    [fetchRequest setFetchBatchSize:20];

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Route" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    NSSortDescriptor *nameSort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)];
    NSArray *sortDescriptors = @[nameSort];
    [fetchRequest setSortDescriptors:sortDescriptors];

    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Routes"];
    self.fetchedResultsController = aFetchedResultsController;
    self.fetchedResultsController.delegate = self;

    NSError *error = nil;
    if (![self.fetchedResultsController performFetch:&error]) {
        NSLog(@"Unresolved error with NSFetchedResultsController: %@", [error description]);
        abort();
    }

    return _fetchedResultsController;
}

这是发生故障的地方:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {

    switch (type) {
            // Data was inserted - insert the data into the table view
        case NSFetchedResultsChangeInsert: {
            [self.savedRoutesTableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

            break;
        }
            // Data was deleted - delete the data from the table view
        case NSFetchedResultsChangeDelete: {
            [self.savedRoutesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
        }
        case NSFetchedResultsChangeUpdate: {
            SavedRoutesTableViewCell *cell = (SavedRoutesTableViewCell *)[self.savedRoutesTableView cellForRowAtIndexPath:indexPath];
            [cell configureCellWithRoute:[controller objectAtIndexPath:newIndexPath]];
            break;
        }
        case NSFetchedResultsChangeMove: {
            [self.savedRoutesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            [self.savedRoutesTableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
        }
        default:
            break;
    }
}

在 didChangeObject 方法中,我的 indexPath 和 newIndexPath 都是 nil。我可以 NSLog 我的对象,我确实看到了实体。它在[self.savedRoutesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];方法中崩溃,但以下情况除外:

CoreData: error: Serious application error.  Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  *** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0] with userInfo (null)

当我保存这个对象时,我像这样保存它:

self.route.name = routeName;
NSManagedObjectContext *tempContext = [self.route managedObjectContext];

[tempContext performBlock:^{
        NSError *error = nil;
        if (![tempContext save:&error]) {
            NSLog(@"an error occurred: %@", [error localizedDescription]);
        }

        [self.managedObjectContext performBlock:^{
            NSError *error = nil;
            if (![_managedObjectContext save:&error]) {
                NSLog(@"error in main context: %@", [error localizedDescription]);
            }
        }];
    }];

我不太确定在哪里调试它,因为NSFetchedResultsController只是没有返回已删除对象的 indexPath。有什么想法吗?提前致谢。

编辑: 好吧,我找到了导致错误的罪魁祸首,但我不确定为什么会这样。基本上我有一个 ViewController,如果它正在编辑路由,它会从主 MOC 接收一个 Route 实体,或者如果你正在创建一个新路由,它会插入一个新实体。所以在那个 viewController 中,如果我正在编辑一条路线,因为我正在尝试为其父级使用两个 MOC,一个临时和一个主,所以如果用户决定取消而不创建新路线,我可以轻松扔掉东西,我需要将该路线转移到其他上下文以使其他代码工作。所以“转移”看起来像:

    NSManagedObjectContext *moc = _route.managedObjectContext;
    NSManagedObjectID *routeId = [_route objectID];
    self.tempContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    self.tempContext.parentContext = moc;
    NSManagedObject *localRoute = [self.tempContext objectWithID:routeId];
    self.route = localRoute;

使用此代码,我现在可以在现有路线中添加位置,因为这些位置位于相同的上下文中,但不知何故,它会搞砸从主 MOC 中删除现有路线。不知道为什么以及最好的解决方案是什么。

4

2 回答 2

0

有同样的问题并通过确保 NSFetchedResultsController 始终使用相同的 NSManagedContext 来解决它。例如,如果您使用 fetch 控制器从数据库中获取对象,然后您想删除该对象,请确保 fetch 控制器使用它在获取期间使用的相同托管上下文。

NSFetchedResultsController 已针对 UITableView 进行了优化,而 UITableView 是用户界面组件,用户界面应始终由主线程处理,因此无需每次进入 fetch 时都创建新的 NSManagedContext ...因此实现此代码应该可以解决此问题问题:

@synthesize fetchedResultsController = __fetchedResultsController; @synthesize managedObjectContext = __managedObjectContext;

- (NSManagedObjectContext *)managedObjectContext
{
    if (__managedObjectContext != nil)
    {
        return __managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = ap.persistentStoreCoordinator;
    if (coordinator != nil)
    {
        __managedObjectContext = [[NSManagedObjectContext alloc] init];
        [__managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return __managedObjectContext;
}

ap 是指向 AppDelegate 的指针:

ap = [[UIApplication sharedApplication] delegate];

这段代码所做的是创建一个 MOC 实例,然后再重用它。警告:这不是线程安全的,它不一定是(因为你应该只在主线程中使用它),所以如果你误用它,它将无法工作......

使用它:

- (NSFetchedResultsController *)fetchedResultsController
{
    if (__fetchedResultsController != nil)
    {
        return __fetchedResultsController;
    }
    //create __fetchedResultsController
    // do some fetching
    return __fetchedResultsController;
}

因此,当您要填充表格时:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [__fetchedResultsController.fetchedObjects count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    NSManagedObject *managedObject = [__fetchedResultsController objectAtIndexPath:indexPath];

    cell.textLabel.text = [managedObject valueForKey:@"smth."];

    return cell;
}

如果您的数据集仅更改:

__fetchedResultsController = nil;
[tableView reloadData]

包括 NSFetchedResultsControllers 委托方法在内的所有内容都可以正常工作,没有 nil indexPaths 等等......

希望我帮助了某人...

于 2014-02-20T11:27:56.393 回答
0
NSManagedObjectContext *moc = _route.managedObjectContext;
NSManagedObjectID *routeId = [_route objectID];
self.tempContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
self.tempContext.parentContext = moc;
NSManagedObject *localRoute = [self.tempContext objectWithID:routeId];
self.route = localRoute;

必须获取与路线关联的上下文。

于 2013-10-11T18:55:43.673 回答