Как программно переключить NSFetchedResultsController UITableView (или его предикат)?


У меня есть UITableView, который отображает подмножество большого количества объектов с именем "документы". Подмножество определяется другой сущностью "Selection". Выборка именуется, упорядочивается список документов.

Это работает нормально, за исключением тех случаев, когда я хочу изменить отображаемый выбор во время выполнения. Я получаю только пустой список.

В принципе, мне нужно изменить предикат, который содержит мой NSFetchedResultsController, чтобы новый предикат использовал другой выбор. Я не мог заставить его работать. Мой последняя попытка-полностью избавиться от NSFetchedResultsController и перераспределить его:

- (void) displaySelection:(Selection *)aSet
{
 self.currentSelection = aSet;
 self.fetchedResultsController = nil;
 // methods here don't all use the property but directly the ivar, so we must trigger the getter
 [self fetchedResultsController];
 [self.tableView reloadData];
}
И конечно, геттер NSFetchedResultsController делает правильную вещь:
- (NSFetchedResultsController *)fetchedResultsController
{
    if (fetchedResultsController != nil) { return fetchedResultsController; }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"DocInSelection" inManagedObjectContext:managedObjectContext];
    [fetchRequest setEntity:entity];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"selection.identifier like %@", currentSelection.identifier];
    [fetchRequest setPredicate:predicate];
<snip>
    [fetchRequest setSortDescriptors:sortDescriptors];
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:@"Root"];
    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;
<snip>
    return fetchedResultsController;
}

Этот код работает в первый раз, потому что начальный выбор установлен. Когда displaySelection: называется, но таблица становится пустой.

Очень похожий вопрос был задан в NSFetchedResultsController fetch request - updating predicate и UITableView

И ответ был: чтобы избавиться от NSFetchedResultsController. Я не хочу этого делать, потому что NSFetchedResultsController приносит много полезных полезностей (например, кэширование, частичная загрузка...). Вопрос все еще стоит: как "переключить" данные в UITableView, поддерживаемом NSFetchedResultsController, где" переключить " означает иметь другой предикат или даже (не в моем случае) другую сущность.

Отметим для полноты, что поскольку отношение "многие-ко-многим" от выбора к документу является упорядоченный, он обрабатывается через промежуточную облегченную сущность, называемую DocInSelection, которая имеет свойство "упорядочивание" и два отношения "многие к одному" для документа и выбора.

Спасибо за любое предложение.

4 3

4 ответа:

Поскольку NSFetchedResultsController (FRC) является объектом, вы можете хранить его экземпляры, как и любой другой объект.

Один полезный метод состоит в том, чтобы инициализировать и сохранить несколько FRC в словаре, а затем установить атрибут fetchedResultController контроллера tableview на нужный вам в данный момент FRC. Это полезно в таких ситуациях, как сегментированный элемент управления для сортировки по различным атрибутам или сущностям в одной таблице. Этот метод имеет преимущество в поддержании отдельных кэшей FRC, которые может ускорить получение значительно.

Просто убедитесь, что вы отправили самому tableview beginUpdates, Прежде чем поменять контроллеры, а затем endUpdates, Когда вы закончите. Это не позволяет таблице запрашивать данные в узком окне при замене FRC. Затем вызовите reloadData.

После того, как я опубликовал свой вопрос, я попробовал вариант кода, который показал ОП другого вопроса. Это работает на меня. Вот ОНО:

- (void) displaySelection:(Selection *)aSet
{
    if (aSet != self.currentSelection) {
        self.currentSelection = aSet;

        NSFetchRequest *fetchRequest = [[self fetchedResultsController] fetchRequest];
        NSPredicate *predicate = nil;
        NSEntityDescription *entity = nil;
        entity = [NSEntityDescription entityForName:@"DocInSelection" inManagedObjectContext:managedObjectContext];
        predicate = [NSPredicate predicateWithFormat:@"selection.identifier like %@", currentSelection.identifier];
        [fetchRequest setEntity:entity];
        [fetchRequest setPredicate:predicate];

        [NSFetchedResultsController deleteCacheWithName:@"Root"];

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

Хотя это может сработать, в справочной библиотеке iOS есть заметка, которая меня беспокоит:

Важно: вы не должны изменять запрос на получение. Например, вы должны не менять свой предикат или вид предварительные заявки.

Источник: Ссылка На Класс NSFetchedResultsController

Это дополнительное Примечание отсутствует в справочной библиотеке iOS 3.2.

Просто хотел указать на это.

Важное замечание: если вы" перезаписываете " объект fetchController, убедитесь, что вы очистили его .делегировать сначала-в противном случае вы получите сбои при удалении строк и т. д., Как старый fetchController и его делегат получают события.