관리 메뉴

사과하는 제라스

[Swift 지식] UITableView Cell의 생명주기(feat.prefetch를 써보자!) 본문

제라스의 Swift 공부/Swift 지식

[Swift 지식] UITableView Cell의 생명주기(feat.prefetch를 써보자!)

Xerath(제라스) 2024. 5. 10. 03:37

목차

    728x90
    반응형

    서론

    안녕하세요~! 개발자 제라스입니다! 🤖🤖🤖

     

    요즘 제가 다시 UIKit을 잡고 있는데...간단한 UITableView에 있어서 재사용하는

    제가 최근에 UITableView를 구현해보고 있었는데 Cell을 재사용하는 과정을 잘 모르고 있더군요...!!! 😭😭

     

    UIKit에서 자주 쓰는 UITableView, UICollectionView에서는 Cell을 따로 재사용하는 방식으로 씁니다.

    만약 Cell을 재사용하지 않는다면 메모리 절약이 어려워지고 성능 또한 저하되게 되기 때문이죠 ㅠㅠ

     

    그럼 이 Cell을 어떤 원리로 재사용이 되고 재사용 시의 Cell은 얼마나 있는지 확인해보겠습니다!

    Cell의 생명주기

    우리는 기본적으로 보이는 화면에서 쓰이는 Cell들과 아직 보여지기 전에 준비 중인 Cell들이 있습니다.

    다음은 정말 유명한 Cell의 생명주기 그림인데요.

     

    위 그림을 참고하면 셀 5~13이 보여지고 있다가 위로 스크롤을 올려서 셀5가 빠지고 셀1이 추가되는 상황입니다.

    이때 셀5는 재사용 queue로 들어가지게 되고 셀1은 준비된 채 보여지게 됩니다.

     

    이런 식으로 우리는 보이지 않는 곳에서 queue로 재사용 cell들을 다루고, 이 재사용 cell들은 일련의 과정을 거쳐서 초기화가 됩니다.

    그럼 정확히 무슨 과정을 거치는지

    - 처음 초기화 상황

    - 스크롤을 내리는 상황

    - 중간에 다시 올리는 상황

    을 기준으로 한번 나눠서 살펴보겠습니다!

     

    처음 실행한 TableView 화면

     

    그럼 Cell의 생명주기를 봅시다.

    한번 예제를 만들었는데요 ㅎㅎ(디자인은...네...이해 부탁드립니다 ㅠ.ㅠ)

     

    다음과 같이 각 Cell이 IndexPath의 row 값을 text로 갖도록 만들었습니다.

    일단 처음화면은 다음과 같습니다.

     

    딱 보면 총 3개의 Cell이 있습니다.

    출력 결과를 보면 다음과 같아요!

    일단 처음에 뭔 놈의 numberOfRowsInSection(UITableDataSource 메소드)이 저리 많이 출력돼???!!!!!

    했었는데...

    알고보니 UITableView를 그리기 위해서 섹션의 개수를 알아내기 위해서 reload되거나 셀 개수를 가져와서 화면을 그리고 데이터를 관리하고자 반복적으로 호출한다고 합니다ㅎㅎ(이건 좀 더 줄일 여지가 있는지 좀 더 알아봐야겠어요...!)

     

    그 다음은 cellForRowAt(UITableDataSource 메소드)이 실행되는데 다음과 같은 코드가 실행되는 거거든요?

    /// cellForRowAt
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        print(#function, ":\(indexPath.row)번째 dequeReusableCell을 만들기 시작!")
        guard let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCell.reuseIdentifier,
                                                       for: indexPath) as? TableViewCell else { return UITableViewCell() }
    
        cell.configure(with: indexPath.row)
    
        let address = String(format: "%p", cell)
        print("Cell의 주소: \(address)")
    
        return cell
    }

     

     

    우선 'N번째 dequeReusableCell을 만들기 시작!'가 출력이 되구요.

     

    여기서 dequeueReusableCell을 이용해서 TableViewCell의 reuseIdentifier를 재사용 식별자로 가진 TableView의 재사용 Queue에서 사용가능한 Cell을 가져와서 cell이라는 상수로 지정해줍니다.

     

    이때 타입은 UITableViewCell로 가져오거든요??

    그래서 TableViewCell 타입으로 변경해줘야 함요...!

     

    그 후 이 Cell에 데이터를 넣어줄 거에요!

    저는 indexPath.row값을 구현해뒀던 TableViewCell의 configure 함수에 넘겨줘서 해당 Cell에 Text를 넣어줬어요!

    (ex. Cell 0, Cell 1, ...)

     

    이렇게 configure한 후에 Cell의 주소를 출력했습니다.

    (이 주소는 더 밑에서 보여주려고 쓴 거라 조금만 WaitWait!)

     

    이렇게 cellForRow 함수가 끝나고 나서는 willDisplay(UITableViewDelegate 메소드)가 실행돼요!

    이 친구는 실제 Cell이 화면에 나타나기 직전에 실행되는데 미리 커스터마이징을 할 수도 있고 미리 보여지기 전에 이 Cell에 대해서 해주고 싶은 작업을 할 수 있는 그런 여지를 주는 친구입니다 ㅎㅎ

     

    자 그럼 딱 정리합시다!

    처음 화면에 나타나는 Cell은

    cellForRowAt - configure - willDisplay

    이 순서로 실행됩니다.

    ㅇㅋ 여기까지는 됐다!

     

    스크롤을 내릴 때

    한번 다음처럼 스크롤을 내려봤어요!

    그.랬.더.니!!!

    이렇게 출력이 나오더라구요!

     

    맨 위부터 보면 prefetchRowsAt(UITableViewPrefetchDataSource 메소드)인데요!

    이 친구가 뭔지부터 보죠!

     

    일단 prefetch는 뭐하는 동작이냐면...

    미리 데이터를 로드해오는 동작입니다!

    즉, 우리가 데이터를 로드를 해오는게 오래 걸리면 화면이 보여져야 할 타이밍에 안보여지니까 미리 여러개 로드해두는 겁니다 ㅎㅎ

    그러면 재료가 없어서 못보여주는 상황을 개선시킬 수 있으니까요~!!

    prefetch는 iOS 10부터 지원을 해주던 건데 다음과 같이 쓸 수 있습니다.

    // MARK: - Prefetch
    extension ViewController: UITableViewDataSourcePrefetching {
        
        /// prefetchRowsAt
        func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
            print(#function, "\(indexPaths)")
        }
        
        /// cancelPrefetchingForRowsAt
        func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
            print(#function)
        }
        
    }

     

    이렇게 쓰면 TableView에 prefetch 기능을 추가해주게 됩니다~!!

    즉, 미리 여러개의 데이터를 불러올 수 있다는 거죠!

     

    prefetchRowsAt은 스크롤 동작을 예측하고 곧 사용될 거라고 예상되는 셀들이 데이터들을 미리 prefetch해올 때 호출됩니다.

    이 예제에서는

    tableView(_:prefetchRowsAt:) [[0, 3], [0, 4], [0, 5], [0, 6], [0, 7], [0, 8], [0, 9], [0, 10], [0, 11], [0, 12]]

     

    이렇게 되어있잖아요?

    즉, 2번 Cell까지 보이는 상황에서 아래로 스크롤을 살짝만 해도 바로 3~12번 Cell이 올 것으로 예상하고 로드해오는 겁니다

    ㄷㄷㄷ(똑똑하구만~~)

     

    이렇게 prefetch한 후에는 여느 때처럼 Cell을 뚝딱뚝딱 만들고 willDisplay까지 실행됩니다.

    cellForRowAt이 있어야 Cell이 완성되어 화면에 보여지거든요? prefetch는 오직 재료만 가져오는, 즉, 데이터만 가져오는 역할을 하기 때문에 우리는 반드시 TableView에서 cellForRowAt을 구현해줘야만 합니다. 항.상~!!

     

    자, 다시 돌아와서 출력된 걸 보면 처음엔 3~12번 셀을 prefetch했는데 후에는 하나씩 더 가져오죠?

    prefetch할 데이터 개수는 iOS 시스템에서 결정하는데 이 예제에서는 10개를 미리 가져오고 그 다음 prefetch부터는 1개씩 새로 가져오는 겁니다.

     

    오호~~그렇구나!!

     

    그러면 cancelPrefetchingForRowsAt(UITableViewPrefetchDataSource 메소드)은 뭐하는 친구냐???

    얘는 우리가 prefetch해왔는데 쓸모가 없어질 때 호출됩니다.

    예를 들면,

    - 스크롤을 반대로 바꾸는 경우 (아래로 스크롤하는 거 보고 밑에 보여질 애들 가져왔는데 갑자기 위로 스크롤하면 이제 쓸모없는 데이터니까 cancel시켜서 불필요한 데이터 처리를 최소화.)
    - 너무 빠른 스크롤을 통해서 가져온 것들은 이미 제대로 보여지기도 전에 지나가버린지라 더 이상 필요없는 데이터들이 되는 경우

     

    이럴 때 cancelPrefetchingForRowsAt가 불립니다.

    이건 다시 위로 올리는 상황에서 한번 다시 보겠습니다!

     

    우리가 지금 여기서 좀 더 스크롤 내려볼게요...!

    tableView(_:prefetchRowsAt:) [[0, 15]]
    tableView(_:cellForRowAt:) :6번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :6번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x106838a00
    tableView(_:willDisplay:forRowAt:) 6
    tableView(_:prefetchRowsAt:) [[0, 16]]
    tableView(_:cellForRowAt:) :7번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :7번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x108825a00
    tableView(_:willDisplay:forRowAt:) 7
    tableView(_:prefetchRowsAt:) [[0, 17]]
    tableView(_:cellForRowAt:) :8번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :8번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x108834e00
    tableView(_:willDisplay:forRowAt:) 8
    tableView(_:prefetchRowsAt:) [[0, 18]]
    tableView(_:cellForRowAt:) :9번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :9번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x10981c200
    tableView(_:willDisplay:forRowAt:) 9
    tableView(_:prefetchRowsAt:) [[0, 19]]
    tableView(_:cellForRowAt:) :10번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :10번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x10d808200
    tableView(_:willDisplay:forRowAt:) 10
    tableView(_:prefetchRowsAt:) [[0, 20]]
    tableView(_:cellForRowAt:) :11번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :11번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x109822600
    tableView(_:willDisplay:forRowAt:) 11
    tableView(_:prefetchRowsAt:) [[0, 21]]
    tableView(_:cellForRowAt:) :12번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :12번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x106838a00
    tableView(_:willDisplay:forRowAt:) 12
    tableView(_:prefetchRowsAt:) [[0, 22]]
    tableView(_:cellForRowAt:) :13번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :13번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x108825a00
    tableView(_:willDisplay:forRowAt:) 13
    tableView(_:prefetchRowsAt:) [[0, 23]]
    tableView(_:cellForRowAt:) :14번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :14번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x108834e00
    tableView(_:willDisplay:forRowAt:) 14
    tableView(_:prefetchRowsAt:) [[0, 24]]
    tableView(_:cellForRowAt:) :15번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :15번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x10981c200
    
    ...(생략)...
    
    tableView(_:prefetchRowsAt:) [[0, 35]]
    tableView(_:cellForRowAt:) :26번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :26번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x108834e00
    tableView(_:willDisplay:forRowAt:) 26
    tableView(_:prefetchRowsAt:) [[0, 36]]
    tableView(_:cellForRowAt:) :27번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :27번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x10981c200

     

    우왁...!!!!!!!

    너무 길죠 ㅋㅋㅋㅋㅋㅋㅋ

    여기서 살펴볼 거는 Cell의 주소와 prepareForReuse입니다.

     

    먼저, Cell의 주소부터 보겠습니다!

    제가 아까 위에서 Cell의 주소는 뒤에서 본다고 했자나여????!

    여기서 보려고 꽁꽁 숨겨둔 겁니다 ㅎㅎㅎ

     

    지금 위에 출력되는 Cell의 주소들을 보면

    0x106838a00
    0x108825a00
    0x108834e00
    0x10981c200
    0x10d808200
    0x109822600
    —————————
    0x106838a00
    0x108825a00
    0x108834e00
    0x10981c200
    0x10d808200
    0x109822600
    —————————
    0x106838a00
    0x108825a00
    0x108834e00
    0x10981c200
    0x10d808200
    0x109822600
    —————————
    0x106838a00
    0x108825a00
    0x108834e00
    0x10981c200
    0x10d808200
    0x109822600
    —————————
    0x106838a00
    0x108825a00
    0x108834e00
    0x10981c200

     

    이렇게 총 6개의 Cell의 주소가 반복됩니다.

    우리가 현재 첫 화면에 3개의 Cell이 보이거든요?

    그래서 그것보다 3개 많은 6개를 Cell로 쓰는 겁니다.

    3개는 보여지고 있는 Cell들이고 나머지 3개는 뒤에서 재사용될 준비를 하고 있는 거죠!!

     

    근데 왜 하필 3개냐...??

    사실 이건 iOS 시스템에서 결정하는 거기에 정확하진 않지만 제가 첫 화면에 20개, 30개 이렇게 보이게 했을 때도

    23개, 33개의 주소들이 나오더라구요!?!?!

    이걸로 봐서는 뒤에서 재사용 준비 중인 Cell 여분으로 display되고 있는 Cell의 개수에 상관없이 3개 정도의 여분 Cell을 만들어두는 것 같습니다!

     

    이건 대략적으로 테스트해본 결과일 뿐 구동 환경, 상황에 따라 매번 달라질 수 있습니다!

     

    이제 prepareForReuse인데요!

     

    얘는 UITableViewCell에서 override할 수 있는 함수입니당~~

    '이름부터가 재사용을 위한 준비?!!!!!! 아니 우리 재사용하는데 얘는 뭔데 필요하지!!!!??' 싶을 수 있는데요...

    사실 이 친구는 재사용될 Cell을 완전 초기화시켜주려고 할 때 쓰입니다!

    물론 재사용 시 이전에 있던 Cell의 이미지나 텍스트를 교체하여 쓸 수도 있지만 미리 초기 상태로 재설정하거나 전에 이 Cell에 적용했던 것들(ex. Cell 배경 색상 등등)을 초기화하려는 겁니다.

     

    예시를 봅시다~!!

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        print("CellForRowAt indexPath: \(indexPath.row)")
        let cell = tableView.dequeueReusableCell(withIdentifier: SearchTableViewCell.reuseIdentifier,
                                                 for: indexPath) as! SearchTableViewCell
    
        if indexPath.row < self.appInfos.count {
            cell.configure(with: appInfos[indexPath.row])
            if indexPath.row == 2 {
                cell.backgroundColor = .brown
            }
        }
        return cell
    }

     

    제가 이런 식으로 indexPath.row가 2인 Cell의 background에 갈색 배경을 넣었습니다.

    근데 만약에 prepareReuse로 배경을 clear로 안바꿔주면,,,

     

    이렇게 이 2번째 Cell이 재사용될 때 갈색이 남아서 2번째 Cell이 아닌 Cell들에서도 갈색이 등장합니다!!

    하지만, 이걸 다음과 같이

    override func prepareForReuse() {
        super.prepareForReuse()
        self.appImageView.image = nil
        self.appTitleLabel.text = nil
        self.appDeveloperLabel.text = nil
        self.appGenresLabel.text = nil
        self.backgroundColor = .clear
    }

    backgroundColor를 clear로 바꾸면......!!

     

    이렇게 원하는대로 2번째 Cell만 갈색이 표시가 됩니다 ㅎㅎㅎ

    prepareForReuse가 이만큼 중요합니다~~~~~~~!👍🏻😭👍🏻

     

    그리고 prepareReuse는 재사용되는 Cell에 적용되는 아이라서 6번째 Cell을 만들때부터 적용이 되고 있습니다.

    ...(생략)...
    tableView(_:prefetchRowsAt:) [[0, 13]]
    tableView(_:cellForRowAt:) :4번째 dequeReusableCell을 만들기 시작!
    init(style:reuseIdentifier:) :TableViewCell의 init을 실행합니다.
    configure(with:) :4번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x10d808200
    tableView(_:willDisplay:forRowAt:) 4
    tableView(_:prefetchRowsAt:) [[0, 14]]
    tableView(_:cellForRowAt:) :5번째 dequeReusableCell을 만들기 시작!
    init(style:reuseIdentifier:) :TableViewCell의 init을 실행합니다.
    configure(with:) :5번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x109822600
    tableView(_:willDisplay:forRowAt:) 5
    tableView(_:prefetchRowsAt:) [[0, 15]]
    tableView(_:cellForRowAt:) :6번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다. // 첫 등장!!
    configure(with:) :6번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x106838a00
    tableView(_:willDisplay:forRowAt:) 6
    tableView(_:prefetchRowsAt:) [[0, 16]]
    tableView(_:cellForRowAt:) :7번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :7번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x108825a00
    tableView(_:willDisplay:forRowAt:) 7
    tableView(_:prefetchRowsAt:) [[0, 17]]
    tableView(_:cellForRowAt:) :8번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :8번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x108834e00
    tableView(_:willDisplay:forRowAt:) 8
    ...(생략)...

     

    이 예제에서는 6번째 Cell부터 등장하고 있죠!!

     

    자 이제는 마지막으로!!!

    중간에 다시 스크롤을 올릴 때를 봅시다~~

    스크롤을 중간에 다시 올리는 상황

    일단 다음과 같이 내리다가 중간에 올리고 또 다시 내리고...이런 동작을 해보았습니다!

     

    그 결과...! 중간에 cancelPrefetchingForRowsAt가 등장하는데요...!!

    ...(생략)...
    tableView(_:cellForRowAt:) :4번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :4번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x10900f800
    tableView(_:cancelPrefetchingForRowsAt:) [[0, 23]] // 등장!!!!!!!
    tableView(_:willDisplay:forRowAt:) 4
    tableView(_:cellForRowAt:) :3번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :3번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x10901ce00
    tableView(_:cancelPrefetchingForRowsAt:) [[0, 22]]
    tableView(_:willDisplay:forRowAt:) 3
    tableView(_:cellForRowAt:) :2번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :2번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x107030e00
    tableView(_:cancelPrefetchingForRowsAt:) [[0, 21]] // 등장!!!!!!!
    tableView(_:willDisplay:forRowAt:) 2
    tableView(_:cellForRowAt:) :1번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :1번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x10701e600
    tableView(_:cancelPrefetchingForRowsAt:) [[0, 20]] // 등장!!!!!!!
    tableView(_:willDisplay:forRowAt:) 1
    tableView(_:cellForRowAt:) :0번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :0번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x10601b800
    tableView(_:cancelPrefetchingForRowsAt:) [[0, 19]] // 등장!!!!!!!
    tableView(_:willDisplay:forRowAt:) 0
    tableView(_:prefetchRowsAt:) [[0, 4], [0, 5], [0, 6], [0, 7], [0, 8], [0, 9], [0, 10], [0, 11], [0, 12], [0, 13]]
    tableView(_:cancelPrefetchingForRowsAt:) [[0, 18]] // 등장!!!!!!!
    tableView(_:prefetchRowsAt:) [[0, 3]]
    tableView(_:willDisplay:forRowAt:) 3
    tableView(_:cellForRowAt:) :4번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :4번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x10900f800
    tableView(_:willDisplay:forRowAt:) 4
    tableView(_:cellForRowAt:) :5번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :5번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x10d010a00
    tableView(_:willDisplay:forRowAt:) 5
    tableView(_:cellForRowAt:) :6번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :6번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x10601b800
    tableView(_:willDisplay:forRowAt:) 6
    tableView(_:cellForRowAt:) :7번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :7번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x10701e600
    tableView(_:willDisplay:forRowAt:) 7
    tableView(_:cellForRowAt:) :8번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :8번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x107030e00
    tableView(_:willDisplay:forRowAt:) 8
    tableView(_:prefetchRowsAt:) [[0, 18]]
    tableView(_:cellForRowAt:) :9번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :9번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x10901ce00
    tableView(_:willDisplay:forRowAt:) 9
    tableView(_:prefetchRowsAt:) [[0, 19]]
    tableView(_:cellForRowAt:) :10번째 dequeReusableCell을 만들기 시작!
    prepareForReuse() :TableViewCell의 prepareForReuse를 실행합니다.
    configure(with:) :10번째 Cell에 데이터를 configure합니다.
    Cell의 주소: 0x10900f800
    tableView(_:willDisplay:forRowAt:) 10
    ...(생략)...

     

    이게 나온 것들을 확인해보면 밑으로 스크롤 하면서 prefetch한 데이터들이 메모리에 쌓여나가는데 반대로 스크롤 하면 또 반대로 위의 것들이 prefetch가 됩니다.

     

    근데....근데!!!

    생각해봅시다! 우리가 이걸 무한정으로 prefetch 가능하면 메모리 낭비가 심해지겠죠...??

    그래서 시스템 상에서 예측을 하는 거에요.

    "어? 나 밑으로 내리길래 밑에꺼 가져와놨었는데...엥 이제는 완전 위로만 스크롤 하잖아? 에잇! 이전 꺼는 갖다버려야겠다...!!"

    이렇게요!

     

    그래서 결국 필요없는 prefetch한 데이터는 다 날려주는 거죠!

     

    그리고 이제 찐찐 마지막!!

    우리 원래 아래로만 스크롤 할 때는 순서대로 번갈아 가면서 Cell의 주소가 나오는 걸 확인했었는데,

    사실 이게 엄청 빠르게 스크롤 하거나 거꾸로 하게 되면 달라집니다.

    '어..!! Queue잖아!! FIFO(First-In, First-Out) 아님???'이라고 할 수 있는데

    사실 TableView의 재사용 Queue는 그렇게 순서에 엄격하게 따르진 않고 가장 최근에 사라진 Cell이 Queue의 맨 뒤로 가서 나중나중에 재사용되는게 아니라 오히려 다시 위로 스크롤하면 그걸 바로 먼저 재사용하는 경우도 있습니다.

     

    상당히 복잡하지만 이것만 기억하면 됩니다.

    성능상, 유저 경험상 최적화된 방식으로 재사용이 되는구나~~ 이렇게요!

     

    마무리

    이렇게 여러 상황에서의 TableView Cell의 생명주기를 다뤄보았습니다!

    이거 사실 꽤 중요하기도 할 텐데 예전에 공부하고는 정리를 안해두었다보니...흐흐흐ㅠㅠㅠㅠ

    Cell의 생명주기를 이해해야 뭔가 다른 곳에서의 개념에서 이해할 때도 쉽게 알아먹지 않을까 싶네요~~!! 😃😃

     

    오늘도 긴 포스팅 읽어주셔서 감사합니다!

    다음에도 좋은 포스팅으로 돌아오겠습니다~!!

    참고

    https://developer.apple.com/documentation/uikit/uitableviewdatasourceprefetching

     

    UITableViewDataSourcePrefetching | Apple Developer Documentation

    A protocol that provides advance warning of the data requirements for a table view, allowing you to start potentially long-running data operations early.

    developer.apple.com

     

    https://developer.apple.com/documentation/uikit/uicollectionviewdatasourceprefetching/prefetching_collection_view_data

     

    Prefetching collection view data | Apple Developer Documentation

    Load data for collection view cells before they display.

    developer.apple.com

     

    https://velog.io/@loinsir/iOS-Cell%EC%9D%98-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0

     

    [iOS] Cell의 생명주기

    우선 재사용되는 셀은 UITableViewCell, UICollectionViewCell 등이 있습니다.재사용 LifeCycle테이블뷰 혹은 컬렉션 뷰에서 데이터 소스에 셀 인스턴스를 요청데이터 소스는 요청마다 재사용 큐에 재사용을

    velog.io

    https://gyuios.tistory.com/72

     

    iOS) prepareForReuse() 사용해서 셀을 초기화해보자

    우리는 셀을 재사용하면서 특정 문제점을 경험해봤을 것이다. 바로 셀이 재사용될 때 발생하는 문제점이다. 원인 tableView(_:cellForRowAt:) delegate 메서드에서 사용하는 dequeueReusableCell(withIdentifier:for:)

    gyuios.tistory.com

     

    https://kiljh.medium.com/ios-%ED%85%8C%EC%9D%B4%EB%B8%94%EB%B7%B0-uitableview-%EC%85%80%EC%9D%98-%EC%9E%AC%EC%82%AC%EC%9A%A9-dequeuereusablecell-532eee9ee5ba

     

    iOS 테이블뷰(UITableView) 셀의 재사용 — dequeueReusableCell

    안녕하세요. 도미닉입니다.

    kiljh.medium.com

    https://jiseok-zip.tistory.com/entry/iOSdequeueReusableCellWithIdentifier-%EC%85%80-%EC%9E%AC%EC%82%AC%EC%9A%A9

     

    [iOS]dequeueReusableCellWithIdentifier-셀 재사용

    이슈 📑 아래 사진처럼 Collection View Cell의 dequeueReusableCellWithIdentifier의 이슈입니다. Cell이 재사용의 될 때 해당 Cell의 데이터도 그대로 재사용이 되었습니다. dequeueReusableCellWithIdentifier 📎 원리 Tab

    jiseok-zip.tistory.com

    https://so-kyte.tistory.com/90

     

    셀의 재사용 메커니즘

    개킹받는 것임 UITableView / UICollectionView를 사용하는 가장 큰 이유는 Cell을 재사용함으로써 보다 효율적으로 UI 및 데이터 관리를 할 수 있기 때문이다. 그런데 이 셀을 사용하다보면 재사용하는 과

    so-kyte.tistory.com

    https://jinnify.tistory.com/58

     

    [iOS] Cell Life Cycle

    간단한 프로젝트를 진행하면서 TableView의 DataSource, Delegate, DataSourcePrefetching에 대한 개념과 알아보고 Cell이 어느 시점에 호출되는지 자세히 알아 보도록 하겠습니다. 먼저 아래와 같은 화면 모양

    jinnify.tistory.com

    https://youbidan-project.tistory.com/148

     

    [Swift]UITableViewDataSourcePrefetching 테이블 뷰셀에 데이터 미리 로드 시키기

    UITableViewDataSourcePrefetching 은 프로토콜의 한 종류로 사용자의 화면에서 보여지기 전에 셀에서 처리해야 하는 연산이 긴 경우 미리 연산을 수행할 수 있게 해주는 DataSource 입니다. 이러한 경우가

    youbidan-project.tistory.com

    https://velog.io/@chagmn/iOS-Prefetch-TableView

     

    [iOS] Prefetch TableView

    UITableViewDataSourcePrefetching를 언제 써야할까?\-> cellForRowAt이 호출되기 전에 미리 데이터를 로드를 해줘야 할 경우에 사용해주면 좋다!예를 들면, 수많은 셀이 있는데 각 셀마다 이미지를 서버에서

    velog.io

     


    아직 꼬꼬마 개발자입니다.

    더 나은 설명이나 코드가 있다면 언제든 환영입니다.

    적극적인 조언과 피드백 부탁드립니다!

     

    그럼 오늘도 개발 가득한 하루되세요!

    - Xerath -

    🤖🤖🤖🤖🤖🤖🤖

     

    728x90
    반응형