В чем разница между обычным Data Source и Diffable Data Source в UIKit?

«В чем разница между обычным Data Source и Diffable Data Source в UIKit?» — вопрос из категории UIKit, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Обычный Data Source (протоколы UITableViewDataSource/UICollectionViewDataSource) требует ручного управления состоянием и обновлениями. Diffable Data Source (классы UITableViewDiffableDataSource/UICollectionViewDiffableDataSource) автоматически вычисляет и анимирует изменения между состояниями данных.

Сравнительная таблица:

Характеристика Обычный Data Source Diffable Data Source
Управление состоянием Вы храните массив данных и вручную синхронизируете его с ячейками. Вы работаете со снимками (Snapshot), которые описывают полное состояние данных.
Обновление UI reloadData() (без анимации) или ручные insert/delete/move вызовы внутри performBatchUpdates. Один вызов apply(_:animatingDifferences:). Система сама вычисляет diff и анимирует изменения.
Идентификация По IndexPath (секция, ряд). По уникальным идентификаторам, соответствующим Hashable.
Безопасность Высокий риск ошибок (несоответствие числа элементов, некорректные IndexPath). Безопасен по дизайну. Нельзя сослаться на несуществующий элемент.
Производительность reloadData() перезагружает все. Ручные обновления эффективны, но сложны в реализации. Diff-алгоритм оптимизирован. Отлично подходит для частых и сложных обновлений.

Пример Diffable Data Source:

// 1. Определите типы для секций и элементов (должны быть Hashable).
enum Section: CaseIterable { case main }
struct Item: Hashable {
    let id: UUID
    let title: String
}

// 2. Создайте источник данных.
var dataSource: UICollectionViewDiffableDataSource<Section, Item>!

dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) {
    (collectionView, indexPath, item) -> UICollectionViewCell? in
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! MyCell
    cell.configure(with: item)
    return cell
}

// 3. Создайте и примените снимок (Snapshot).
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.main])
snapshot.appendItems([item1, item2, item3]) // Массив элементов

// 4. Примените снимок. Изменения будут автоматически анимированы.
dataSource.apply(snapshot, animatingDifferences: true)

// 5. Для обновления просто создайте новый снимок с актуальным состоянием и примените его.
var newSnapshot = NSDiffableDataSourceSnapshot<Section, Item>()
newSnapshot.appendSections([.main])
newSnapshot.appendItems([updatedItem1, item2, newItem4]) // Удалили item3, добавили newItem4
dataSource.apply(newSnapshot, animatingDifferences: true) // Анимация удаления и добавления

Ключевые преимущества Diffable Data Source:

  1. Декларативность: Вы описываете, как должны выглядеть данные, а не какие операции выполнить для обновления.
  2. Надежность: Исключаются классические ошибки Invalid update.
  3. Эффективность: Не нужно вручную вычислять минимальный набор изменений.

Когда использовать обычный Data Source: В очень простых статических таблицах или при жестких требованиях к производительности, где diff-алгоритм может быть излишним.