// 모든 아이템을 담기 위한 컨텐츠 사이즈
// UIScrollView.contentSize를 위해 필요.
open var collectionViewContentSize: CGSize { get }
// 쿼리 메소드들은 성능에 중요한 영향을 미친다.
// 특정 영역에 대한 Attributes 반환
func layoutAtributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
// 특정 인덱스패스에 대한 layoutAttributs 반환
func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
// layout을 invalidate할 때 마다 호출됨
// layoutAttributes를 캐싱해놓고, collectionViewContentSize를 구해둬야 한다.
func prepare()
// bound가 변할 때마다 호출된다. -> 사이즈 변경, 원점 변경
// 스크롤 중에도 호출된다.
// 기본 구현은 false다.
func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool
collectionView의 업데이트 순서는 중요하지 않다.
Move와 delete가 같은 indexPath에서 이루어지는 경우
Move와 insert가 같은 indexPath에서 이루어지는 경우
Move를 같은 indexPath에 대해서 2번 이상 요청하는 경우
유효하지 않은 indexPath를 지정하는 경우
하지만 datasource의 업데이트 순서는 중요하다.