ios - NSFetchedResultsController 未加载所有条目

标签 ios iphone uitableview core-data nsfetchedresultscontroller

我有一个简单的应用程序,每个 UITabBar 中都有一个 UITabBarController 和 2 个 UITableViewControllers。第一个 UITableViewController 称为 Videos,第二个称为 Languages

Videos 将只代表有视频的语言,Languages 将显示所有语言。

出于测试目的,我从 AppDelegate 填充核心数据实体。

我的核心数据模型如下:

enter image description here

填充核心数据的方法如下所示:

- (void)loadVideosTab
{
    // Here is where we'll load up the Videos tab.
    NSManagedObjectContext *context = [self managedObjectContext];
    NSString *chinese = @"Chinese";
    NSString *dutch = @"Dutch";
    NSString *english = @"English";
    NSString *french = @"French";
    NSString *italian = @"Italian";
    NSString *punjabi = @"Punjabi";
    NSString *spanish = @"Spanish";
    NSString *tamil = @"Tamil - தமிழ்";

    Language *language = [NSEntityDescription insertNewObjectForEntityForName:@"Language" inManagedObjectContext:context];


    Video *chineseLanguage = (Video *)[Video videoLanguage:chinese inManagedObjectContext:context];
    language.videos = chineseLanguage;
    Video *dutchLanguage = (Video *)[Video videoLanguage:dutch inManagedObjectContext:context];
    language.videos = dutchLanguage;
    Video *englishLanguage = (Video *)[Video videoLanguage:english inManagedObjectContext:context];
    language.videos = englishLanguage;
    Video *frenchLanguage = (Video *)[Video videoLanguage:french inManagedObjectContext:context];
    language.videos = frenchLanguage;
    Video *italianLanguage = (Video *)[Video videoLanguage:italian inManagedObjectContext:context];
    language.videos = italianLanguage;
    Video *punjabiLanguage = (Video *)[Video videoLanguage:punjabi inManagedObjectContext:context];
    language.videos = punjabiLanguage;
    Video *spanishLanguage = (Video *)[Video videoLanguage:spanish inManagedObjectContext:context];
    language.videos = spanishLanguage;
    Video *tamilLanguage = (Video *)[Video videoLanguage:tamil inManagedObjectContext:context];
    language.videos = tamilLanguage;

    NSError *error = nil;
    if (![context save:&error])
    {
        // Error
    }    
}

我在 Video 实体上调用的类别是:

+ (Video *)videoLanguage:(NSString *)name inManagedObjectContext:(NSManagedObjectContext *)context
{
    Video *video = nil;

    // Creating a fetch request to check whether the video already exists, calling from the AppDelegate
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Video"];
    request.predicate = [NSPredicate predicateWithFormat:@"language = %@", name];
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"language" ascending:YES];
    request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];

    NSError *error = nil;
    NSArray *videoTitles = [context executeFetchRequest:request error:&error];
    if (!videoTitles)
    {
        // Handle Error
    }
    else if (![videoTitles count])
    {
        // If the video count is 0 then create it
        video = [NSEntityDescription insertNewObjectForEntityForName:@"Video" inManagedObjectContext:context];
        video.language = name;

    }
    else
    {
        // If the object exists, just return it
        video = [videoTitles lastObject];
    }
    return video;

}

所以在 Video 选项卡中,我使用 NSFetchedResultsController 是这样的:

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


    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Video" inManagedObjectContext:managedObjectContext];
    fetchRequest.entity = entity;
    NSPredicate *d = [NSPredicate predicateWithFormat:@"language != nil"];
    [fetchRequest setPredicate:d];
    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"language" ascending:YES];


    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;
}

cellForRow 是:

VideoTabTableViewCell *customCell = (VideoTabTableViewCell *)cell;

Video *video = [self.fetchedResultsController objectAtIndexPath:indexPath];

customCell.videoTabCellLabel.text = video.language;
customCell.videoTabCellLabel.textColor = [UIColor whiteColor];

这成功地引入了语言。

现在,出于测试目的,我希望“语言”选项卡执行完全相同的操作。我有一个 Language 实体的原因是因为“语言”选项卡将是具有视频和传单的语言的组合。

所以在 Languages 选项卡中,我的 fetchedResultsController 是:

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


    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Language" inManagedObjectContext:managedObjectContext];
    fetchRequest.entity = entity;
//    NSPredicate *d = [NSPredicate predicateWithFormat:@"name != nil"];
//    [fetchRequest setPredicate:d];
    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"videos.language" ascending:YES];


    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;
}

cellForRow 是:

VideoTabTableViewCell *customCell = (VideoTabTableViewCell *)cell;

Language *languages = [self.fetchedResultsController objectAtIndexPath:indexPath];

customCell.videoTabCellLabel.text = languages.videos.language;
customCell.videoTabCellLabel.textColor = [UIColor whiteColor];

更新 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"langauges tab";

    VideoTabTableViewCell *cell = (VideoTabTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    [self configureCell:cell atIndexPath:indexPath];
    return cell;

}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    id  sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects];
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    [self.languagesTabTableView 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.languagesTabTableView;

    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.languagesTabTableView endUpdates];
}

我正在从 viewDidLoad 调用 fetchedResultsController 并从 viewWillAppear 重新加载表格。

但是,Languages 选项卡上的情况是它只显示我最后添加的语言,每次启动时,我都会创建另一个新的空单元格。

enter image description here

我只是想不通这究竟是怎么回事。

澄清一下,这里的主要问题不是幻影单元格,而是为什么语言只选择最后一个条目而不是其他条目。

任何指导将不胜感激。

最佳答案

我认为您需要改进数据模型:

首先,从 LanguageVideo 的关系有一个复数名称,videos,这表明您需要每个 Language 对象与许多 Video 对象相关。但是模型编辑器中的图像显示该关系当前定义为“一对一”。

其次,为什么在 Video 实体上有一个 Language 实体, language 属性?从您的代码看来, language 属性是一个表示语言名称的字符串。我建议您应该在 Language 实体上设置一个 name 属性。

除了这些要点之外,问题的症结在于您的 loadVideosTab 方法。它只创建一个 Language 对象:

Language *language = [NSEntityDescription insertNewObjectForEntityForName:@"Language" inManagedObjectContext:context];

然后创建(或获取)多个 Video 对象,并建立与(一个)Language 的关系,例如:

Video *chineseLanguage = (Video *)[Video videoLanguage:chinese inManagedObjectContext:context];
language.videos = chineseLanguage;

但是,因为videos 关系是“一对一”的,Language 对象只能与一个Video 相关。所以每次设置关系时,旧的都会被删除。在您的方法结束时,您的一个 Language 仅与泰米尔语 Video 对象相关。其他七个 Video 对象与任何 Language 无关。

每次运行都会创建一个新的Language,并建立与泰米尔语Video 的关系。因为每个 Video 只能有一个 Language,当建立这种关系时,CoreData 会删除泰米尔语视频与“旧”Language 对象的关系。因此,您最终会得到几个与任何 Video 无关的“旧”Language 对象 - 这些是您的 Language TableView 中的空白行。

编辑

关于处理对多关系的注意事项:该关系表示为 NSSet。如果您正在设置关系(并希望删除现有关系),您可以使用:

language.videos = [NSSet setWithObject:tamilLanguage];

或者一次设置多个对象,使用:

language.videos = [NSSet setWithArray:@[chineseLanguage, dutchLanguage, tamilLanguage]];

这些破坏了现有的关系,所以要添加一个关系,同时保持现有的关系,使用NSMutableSet:

NSMutableSet *videos = [language mutableSetValueForKey:@"videos"];
[videos addObject:tamilLanguage];

(对于 removeObject 也类似,只删除一个相关对象)。我发现这些方法相当不直观,所以如果我有一个反函数是“一对一”的关系,我会改用它:

tamilLanguage.videoLanguage = 语言;

话虽如此,我现在想知道这种反向关系是否也应该是“对多”- Video 可以有很多 Language 吗?

关于ios - NSFetchedResultsController 未加载所有条目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33784967/

相关文章:

ios - 为什么我的指标 View 没有显示?

ios - 解析 : log out anonymous user when user force terminates app

ios - 每次单击选项卡栏项目时如何刷新 View ?

ios - 是否可以调用另一个 View Controller 的tableview didSelect索引路径并执行它?

iphone - 如何推送一个 View ,返回并返回 View ?

objective-c - UITableViewCell 上的 SIG_ABRT 删除

android - 证书和 keystore

iphone - UISearchBar透明背景 View

iphone - 从地址簿添加联系人时 EXC_BAD_ACCESS?

ios - 使用非公共(public) API LSApplicationWorkspace 拒绝应用程序