Core Data 和 NSFRC 显示条目,但在每次启动时都不会持久存在,并且出现 Key-Value 编码错误

Core Data and NSFRC showing entries but not persisting through every launch and a Key-Value Coding Error

我正在构建一个非常简单的应用程序,允许用户在应用程序中浏览有关特定主题的传单和视频。我带来的功能之一是能够将传单或视频标记为收藏夹。

该应用程序 UITabBar 有 5 个选项卡,每个选项卡由 UITableViewController 表示。当用户点击以按住选项卡中的单元格时,它会将其标记为 "starred" 并使用 Core DataNSFetchedResultsController,目的是让该条目出现在已加星标中标签。

这是我的简单核心数据模型:

因此,当用户在 4 个选项卡中的任何一个中点击并按住一个单元格时,这就是代码 I 运行:

- (void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerRightUtilityButtonWithIndex:(NSInteger)index
{

    switch (index) {
        case 0:
        {

            NSIndexPath *indexPath = [self.tableView indexPathForCell:cell]; 

            CustomLeafletVideoTableViewCell *cell = (CustomLeafletVideoTableViewCell*)[self.tableView cellForRowAtIndexPath:indexPath];

            NSString *cellTitle = cell.customCellLabel.text;
            [self moreButtonPressed:cellTitle];
            [cell hideUtilityButtonsAnimated:YES];

            break;
        }
        default:
            break;
    }
}

- (void)cellPressed:(NSString *)passedString
{
    NSManagedObjectContext *context = [self managedObjectContext];

    Item *item = [NSEntityDescription insertNewObjectForEntityForName:@"Item" inManagedObjectContext:context];
    Videos *videos = [NSEntityDescription insertNewObjectForEntityForName:@"Videos" inManagedObjectContext:context];
    videos.title = passedString;
    item.video = videos;

    NSLog(@"Passed String = %@", videos.title); 

}

我创建了一个 FavouritesTableViewController class,这里是主要代码:

- (NSManagedObjectContext *)managedObjectContext
{
    NSManagedObjectContext *context = nil;
    id delegate = [[UIApplication sharedApplication] delegate];
    if ([delegate performSelector:@selector(managedObjectContext)])
    {
        context = [delegate managedObjectContext];
    }
    return context;
}

- (NSFetchedResultsController *)fetchedResultsController
{
    NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
    if (_fetchedResultsController != nil)
    {
        return _fetchedResultsController;
    }
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];


    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Videos" inManagedObjectContext:managedObjectContext];
    fetchRequest.entity = entity;
    NSPredicate *d = [NSPredicate predicateWithFormat:@"items.video.@count !=0"];
    [fetchRequest setPredicate:d];
    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:NO];


    fetchRequest.sortDescriptors = [NSArray arrayWithObject:sort];
    fetchRequest.fetchBatchSize = 20;
    NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
    self.fetchedResultsController = theFetchedResultsController;
    _fetchedResultsController.delegate = self;
    return _fetchedResultsController;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    NSError *error; 
    if (![[self fetchedResultsController] performFetch:&error])
    {
        //exit(-1);
    }
    self.favouritesTableView.dataSource = self;
    self.favouritesTableView.delegate = self;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self.favouritesTableView reloadData];
}
#pragma mark - Table view data source


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    id  sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects];
}


#pragma mark Cell Configuration
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{

    CustomLeafletVideoTableViewCell *customCell = (CustomLeafletVideoTableViewCell *)cell;
    Videos *videos = [self.fetchedResultsController objectAtIndexPath:indexPath];
    NSLog(@"What is the video . title %@", videos.title); 
    customCell.customCellLabel.text = videos.title;
//


}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"FavouritesCell";
    CustomLeafletVideoTableViewCell *cell = (CustomLeafletVideoTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    [self configureCell:cell atIndexPath:indexPath];
    return cell;
}

#pragma mark NSFetchedResultsControllerDelegate Methods

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    [self.tableView beginUpdates];
}

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
    // The boiler plate code for the NSFetchedResultsControllerDelegate

    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;

        default:
            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];
}

问题

我的问题似乎源于 NSFetchedResultsController。在那种方法中,如果我将它与谓词保持原样,当我点击以将单元格保留在另一个选项卡上时,应用程序崩溃并显示:

Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Videos 0x7ffb48d0b910> valueForUndefinedKey:]: the entity Videos is not key value coding-compliant for the key "@count".'

如果我删除谓词行:

//    NSPredicate *d = [NSPredicate predicateWithFormat:@"items.video.@count !=0"];
//    [fetchRequest setPredicate:d];

当我点击以按住一个单元格时,它会将其标记为收藏,然后当我转到“收藏夹”选项卡时,条目就在那里。但是,如果我再次启动该应用程序,“收藏夹”选项卡中的条目就会消失。

我不太确定这里发生了什么。本质上,“收藏夹”选项卡是一个用于存储用户在其他选项卡中加星标的项目的地方。我是否需要谓词?如果不需要,为什么每次启动时数据都不会持久存在?

应用设置时选择了 Core Data,因此 AppDelegate 已正确设置。

任何关于这方面的指导将不胜感激。

video 属性 和 Item 是一对一的关系。基本上它是一个指向对象(或 nil)的指针。你不能算,它不是对象的集合

所以你的关键路径 items.video.@count 尤其是 video.@count 没有意义,因此崩溃。

如果要检查给定 Item 是否存在 video,请使用 @"items.video != nil"

此外,您可能应该遵循约定,对对象名称使用单数(LeafletVideo),对一对一关系使用单数(item 而不是 items).