UICollectionViewDiffableDataSource의 identifier가 Hashable 해야하는 이유가 뭘까?

jane·2022년 9월 6일
0

iOS

목록 보기
32/32

diffable datasource... 너 대체 뭔데?

UICollectionViewDiffableDataSource가 UICollectionViewDataSource와 다른점?

우선 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를 만들 수 있을까?

Hashable

diffable datasource를 사용해 봤다면 값을 identifier로 사용하기 위해서는 Hashable해야한다는 말을 많이 들어봤을 것이다.

그래서 Hashable이 뭔데. 왜 변하지 않는데?

타입이 Hashable하다는 것은...값이 해시함수에 들어가서 정수 해시값으로 변경될 수 있다는 것을 의미한다. 변환된 해시값은 해시테이블의 key로 사용될 수 있다.

해시 테이블은 마치 dictionary와 같은 방법으로 key값을 index로 사용해서 value를 저장한다.
그럼 key값만 가지고 있으면 아주 쉽게 해시 테이블에 저장된 value를 꺼내올 수 있어서 시간 복잡도가 O(1)이다.

해시 테이블은 Dictionary나 Set처럼 순서가 없지만, 내부적으로는 배열로 구현되어있어서 index가 있다.

아니 순서가 없다면서 무슨 index가 갑자기 나와? 할 수 있겠지만
해시함수가 있어서 가능한 일이다.

그림의 예시를 보자면 James라는 키가 해시함수에 들어가면 04라는 해시값(index)로 변환하는 모습을 볼 수 있다.
해시함수가 키를 해시 주소값(=index)로 변환을 해주어서 해시 테이블 내부적으로 인덱스가 존재할 수 있었던 것이다.

그렇다면 여기서 드는 의문은 Hashable은 왜 Equatable을 상속받을까?

Equatable

커스텀 타입중에

  • 모든 저장 프로퍼티가 Hashable한 Struct나
  • 모든 연관값이 Hashable한 enum

을 제외하고는 Hashable 프로토콜을 채택하려면

Hashable이 상속받고 있는 Equatable의 요구사항인 ==와
Hashable의 요구사항인 hash(into:)를 구현해야한다.

Equatable 프로토콜을 채택한다면 두 객체가 서로 같은 값을 가졌는지 비교할 수 있다.

같은 값은 해시함수에 들어가면 무조건 같은 해시값으로 계산되지만,
두 값이 같은 해시값을 가졌다고 해서 무조건 두 값이 같은 것은 아니다.
그러니깐 서로 완전 다른 두 값이 우연히 같은 해시값으로 변환될 수 있다는 것이다.

이것을 해시 충돌이라고 하는데, Hash함수는 단순히 어떤 객체를 정수의 키값으로 변환하는 역할만 하기때문에 두 객체가 서로 같은지는 Equatable을 이용하여 비교해야하는 것이다.

마지막으로 Diffable DatatSource에서 identifiable을 사용하는 경우에 대해 살펴보자.

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의 성능을 최적화한다고 한다.

profile
제가 나중에 다시 보려고 기록합니다 ✏️

0개의 댓글