diffable datasource... 너 대체 뭔데?
우선 UICollectionViewDiffableDataSource는 iOS 13부터 적용이 가능한 CollectionView의 datasource이다. 기본적으로 기존 DataSource와 역할은 같다. CollectionView를 구성하는 데이터를 관리하고, UI를 업데이트하는 역할을 한다.
하지만 업데이트하는 방식에서 차이가 있다.
DiffableDataSource는 이름에서도 알 수 있듯이, 데이터의 Diff 한 부분을 파악해서 달라진 부분만 업데이트하는 것이 가능하다.
이게 가능한 이유는 데이터를 식별하는 방법의 차이 때문인데,
기존의 DataSource는 Section과 Items의 "위치를 나타내는 indexPath"를 들고 있어서 만약 데이터가 수정, 삭제된다면 indexPath도 변경될 수 있기 때문에 불안정한 정보를 가진다. 특정 indexPath에 반드시 해당 값이 존재한다고 확신할 수가 없기 때문에 reloadData()를 할 때, 어떤 데이터가 변했는지 datasource에서 파악할 수가 없어서 모든 데이터를 다시 로딩하여 컬렉션뷰를 업데이트한다.
이러한 한계를 극복하기 위해, DiffableDataSource는 Section과 Items의 "바뀌지 않는 identifier"를 가지도록 구성되었다. item을 identifier로 식별할 수 있게 되면서, 특정 item이 변경된다면 datasource는 변경된 item이 무엇인지 파악할 수 있게 된다.
구체적으로 변경된 item이 무엇인지 파악하는 방법은,
현재 상태를 나타내는 snapshot과 이전 상태를 나타내는 snapshot을 비교해서 달라진 item을 파악한다.
이때 비교는 바로 identifier로 할 수 있다.
이렇게 어떤 item이 달라졌는지 파악이 된다면, 그 부분만 다시 로딩함으로써 애니메이션 효과도 자동으로 적용이 가능해진다.
DataSource와 비교해서 DiffableDataSource가 안정적인 이유는 변하지 않는 identifier 덕분인데, 그렇다면 어떻게 변하지 않는 identifier를 만들 수 있을까?
diffable datasource를 사용해 봤다면 값을 identifier로 사용하기 위해서는 Hashable해야한다는 말을 많이 들어봤을 것이다.
그래서 Hashable이 뭔데. 왜 변하지 않는데?
타입이 Hashable하다는 것은...값이 해시함수에 들어가서 정수 해시값으로 변경될 수 있다는 것을 의미한다. 변환된 해시값은 해시테이블의 key로 사용될 수 있다.
해시 테이블은 마치 dictionary와 같은 방법으로 key값을 index로 사용해서 value를 저장한다.
그럼 key값만 가지고 있으면 아주 쉽게 해시 테이블에 저장된 value를 꺼내올 수 있어서 시간 복잡도가 O(1)이다.
해시 테이블은 Dictionary나 Set처럼 순서가 없지만, 내부적으로는 배열로 구현되어있어서 index가 있다.
아니 순서가 없다면서 무슨 index가 갑자기 나와? 할 수 있겠지만
해시함수가 있어서 가능한 일이다.
그림의 예시를 보자면 James라는 키가 해시함수에 들어가면 04라는 해시값(index)로 변환하는 모습을 볼 수 있다.
해시함수가 키를 해시 주소값(=index)로 변환을 해주어서 해시 테이블 내부적으로 인덱스가 존재할 수 있었던 것이다.
커스텀 타입중에
을 제외하고는 Hashable 프로토콜을 채택하려면
Hashable이 상속받고 있는 Equatable의 요구사항인 ==와
Hashable의 요구사항인 hash(into:)를 구현해야한다.
Equatable 프로토콜을 채택한다면 두 객체가 서로 같은 값을 가졌는지 비교할 수 있다.
같은 값은 해시함수에 들어가면 무조건 같은 해시값으로 계산되지만,
두 값이 같은 해시값을 가졌다고 해서 무조건 두 값이 같은 것은 아니다.
그러니깐 서로 완전 다른 두 값이 우연히 같은 해시값으로 변환될 수 있다는 것이다.
이것을 해시 충돌이라고 하는데, Hash함수는 단순히 어떤 객체를 정수의 키값으로 변환하는 역할만 하기때문에 두 객체가 서로 같은지는 Equatable을 이용하여 비교해야하는 것이다.
마지막으로 Diffable DatatSource에서 identifiable을 사용하는 경우에 대해 살펴보자.
Identifiable 프로토콜을 채택하면 id프로퍼티를 필수로 구현해야한다.
(클래스의 경우에는 자동으로 구현해준다고 한다)
값 타입의 경우에는 id프로퍼티를 따로 구현해야하는 것이다.
요 id프로퍼티는 Hashable해서 안정적인 identity를 가진다.
지난번 포스팅에서 Diffable DatatSource에 identifier를 struct로 채택하지 않고 Identifiable을 채택한 struct의 AssociatedType인 ID로 채택하였다.
만약 저 구조체의 title이나 numberOfLikes가 변경되면서 collectionview가 수정되어 업데이트되어야할때 만약 DestinationPost 구조체 자체가 identifier가 된다면...
안정적인 identity라고 볼 수 없다.
공식문서에 따르면 identifier 타입이 간단하기 때문에 diffable data source의 성능을 최적화한다고 한다.