Ответ
Обычный 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:
- Декларативность: Вы описываете, как должны выглядеть данные, а не какие операции выполнить для обновления.
- Надежность: Исключаются классические ошибки
Invalid update. - Эффективность: Не нужно вручную вычислять минимальный набор изменений.
Когда использовать обычный Data Source: В очень простых статических таблицах или при жестких требованиях к производительности, где diff-алгоритм может быть излишним.