NSFetchedResultsController: Подробное описание

NSFetchedResultsController – это класс в фреймворке Core Data, который предоставляет механизм для эффективного управления отображением данных из NSManagedObjectContext в пользовательском интерфейсе, особенно в табличных представлениях (UITableView) и коллекционных представлениях (UICollectionView). Он тесно интегрирован с концепцией “fetched properties” и обеспечивает автоматическое обновление UI при изменениях в базе данных.

Основные задачи и преимущества

  • Автоматическое обновление UI: Основная задача – отслеживать изменения в наборе результатов запроса (fetch request) и автоматически обновлять пользовательский интерфейс, когда данные добавляются, удаляются или изменяются. Это избавляет разработчика от необходимости вручную перегружать таблицу/коллекцию при каждом изменении данных.
  • Эффективность: NSFetchedResultsController оптимизирован для работы с большими наборами данных. Он использует механизм “faulting” Core Data, что означает, что объекты загружаются из базы данных только тогда, когда они действительно нужны для отображения.
  • Управление секциями и сортировкой: Позволяет легко определять разделы (sections) в табличном представлении на основе определенных атрибутов объектов и управлять сортировкой данных.
  • Интеграция с делегатами: Использует протокол NSFetchedResultsControllerDelegate для уведомления о изменениях в данных, что позволяет реализовать логику обновления UI.
  • Упрощение кода: Значительно упрощает код, связанный с отображением и обновлением данных Core Data в табличных представлениях/коллекционных представлениях.

Как работает NSFetchedResultsController

NSFetchedResultsController не является заменой NSManagedObjectContext или NSFetchRequest. Он использует их для получения и управления данными, но добавляет дополнительный уровень абстракции для упрощения работы с UI.

  1. Создание Fetch Request: Сначала необходимо создать NSFetchRequest, который определяет, какие объекты нужно получить из базы данных. В fetch request указываются сущность (entity), фильтры (predicate) и параметры сортировки (sort descriptors).
  2. Инициализация NSFetchedResultsController: Затем создается экземпляр NSFetchedResultsController, который инициализируется с использованием NSManagedObjectContext, NSFetchRequest и опционального кеша (cache name).
  3. Установка делегата: Реализуйте протокол NSFetchedResultsControllerDelegate и установите экземпляр вашего класса в качестве делегата для NSFetchedResultsController. Это позволит вам получать уведомления об изменениях данных.
  4. Выполнение Fetch Request: Вызовите метод performFetch(), чтобы выполнить fetch request и заполнить кеш результатов. Этот метод может занять некоторое время, особенно при работе с большими наборами данных, поэтому рекомендуется выполнять его в фоновом режиме.
  5. Использование результатов в UI: Используйте методы NSFetchedResultsController (например, objectAtIndex(at:), sections()) для получения объектов и информации о секциях для отображения в табличном представлении/коллекционном представлении.
  6. Обработка изменений: Когда данные изменяются (добавление, удаление, изменение), Core Data автоматически уведомляет NSFetchedResultsController. Он, в свою очередь, уведомляет делегата, который должен обновить UI соответствующим образом.

Пример использования

Предположим, у нас есть сущность “Employee” с атрибутами “name”, “salary” и “department”. Мы хотим отобразить список сотрудников в табличном представлении.

#import <CoreData/CoreData.h>

@interface EmployeesViewController : UITableViewController <NSFetchedResultsControllerDelegate>

@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;

@end

@implementation EmployeesViewController

- (void)viewDidLoad {
[super viewDidLoad];

// 1. Создание Fetch Request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Employee"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
[fetchRequest setSortDescriptors:@[sortDescriptor]];

// 2. Инициализация NSFetchedResultsController
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"department" cacheName:nil];
self.fetchedResultsController.delegate = self;

// 3. Выполнение Fetch Request
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(@"Error performing fetch: %@", error);
}
}

#pragma mark - UITableViewDataSource methods

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

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// ... код для создания и настройки ячейки ...
Employee *employee = [self.fetchedResultsController objectAtIndex:indexPath.row];
cell.textLabel.text = employee.name;
return cell;
}

#pragma mark - NSFetchedResultsControllerDelegate methods

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

@end



Swift

import CoreData

class EmployeesViewController: UITableViewController, NSFetchedResultsControllerDelegate {

    var fetchedResultsController: NSFetchedResultsController <Employee>!

    override func viewDidLoad() {
        super.viewDidLoad()

        // 1. Создание Fetch Request
        let fetchRequest = NSFetchRequest<Employee>(entityName: "Employee")
        let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
        fetchRequest.sortDescriptors = [sortDescriptor]

        // 2. Инициализация NSFetchedResultsController
        fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: "department", cacheName: nil)
        fetchedResultsController.delegate = self

        // 3. Выполнение Fetch Request
        do {
            try fetchedResultsController.performFetch()
        } catch {
            print("Error performing fetch: \(error)")
        }
    }

    // MARK: - UITableViewDataSource methods

    override func numberOfSections(in tableView: UITableView) -> Int {
        return fetchedResultsController.sections?.count ?? 0
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let sectionInfo = fetchedResultsController.sections?[section]
        return sectionInfo?.numberOfObjects ?? 0
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // ... код для создания и настройки ячейки ...
        let employee = fetchedResultsController.object(at: indexPath.row)
        cell.textLabel?.text = employee.name
        return cell
    }

    // MARK: - NSFetchedResultsControllerDelegate methods

    func controllerDidChangeContent(_ controller: NSFetchedResultsController<Employee>) {
        tableView.reloadData()
    }
}

Альтернативы

Хотя NSFetchedResultsController является мощным инструментом, существуют и другие способы управления отображением данных Core Data в UI:

  • Ручное обновление: Можно вручную обновлять таблицу/коллекцию при каждом изменении данных. Это требует больше кода, но может быть полезно в простых случаях или когда требуется более гибкий контроль над процессом обновления.
  • RxSwift/Combine: Использование реактивных фреймворков для отслеживания изменений Core Data и автоматического обновления UI.

Заключение

NSFetchedResultsController – это ценный инструмент для разработчиков, работающих с Core Data. Он упрощает управление отображением данных в пользовательском интерфейсе, обеспечивает высокую производительность и избавляет от необходимости вручную обновлять UI при изменениях данных. Однако важно понимать принципы его работы и правильно настроить fetch request и делегата для достижения оптимальных результатов.


Будет интересно

Nikulux в Telegram

Статьи, архивы материалов, скоро ещё и мемы

(= Подписаться =)