Search
♻️

[iOS] TableView Cell 의 재사용 생명주기 파헤치기 -LifeCycle

UITableViewCell 의 LifeCycle

테이블 뷰에서 셀은 재사용됩니다.
재사용된다는 말은 무엇일까요?
예를들어 데이터의 개수가 1000개 존재한다고 생각해 봅시다.
이 1000개의 데이터에 대한 cell view 객체가 만들어진다면,,, 아이폰의 메모리가 부족할거에요,,,
정작 우리가 한 눈에 볼 수 있는 셀은 많아야 20개일 텐데 말이죠.,,
그래서 TableView 에서는 cell view 객체를 1000개 다 만들어 놓는 것이 아닌 적당한 개수만큼 만들어놓고 재사용한답니다!
이렇게요!
스크롤해서 우리눈에 보이지 않게 된 셀들은 아래로 가서 다시 우리눈에 보여질 준비를한답니다.
메모리를 엄청 아낄 수 있겠죠??
하지만 이과정에서 문제점이 몇가지 생긴다고 하네요,,,(인생은 역시 제로섬 게임,,,)
대표적인 문제가 바로
"셀의 UI 나 속성(attribute) 들이 중첩되거나 반복되는 경우"입니다
눈에서 사라진 셀이 아래로 내려가서 재사용된다고 했죠?
재사용 되는 과정에서 속성이나 UI 를 초기화 시켜주는 코드가 추가적으로 필요하답니다
이러한 문제점을 이해하고 적절하게 대응하기 위해서는
cell 의 life cycle 에 대해서 이해할 필요가 있어요 ㅎ
바로 설명 시작하겠습니다!
설명에 앞서 아래와 같이 간단한 테이블 뷰 예제 코드를 준비했습니다.
//ViewController.swift import UIKit class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! let data = Array(1...50) override func viewDidLoad() { super.viewDidLoad() tableView.delegate = self tableView.dataSource = self tableView.prefetchDataSource = self // Do any additional setup after loading the view. } } extension ViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return data.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell cell.index.text = String(data[indexPath.row]) print("cellForRowAt: \(indexPath.row)") return cell } } extension ViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { print("cell \(indexPath.row) : will display") } func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) { print("cell \(indexPath.row) : didEndDisplaying") } } extension ViewController: UITableViewDataSourcePrefetching { func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) { print("prefetch : \(indexPaths)") } func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) { print("cancelPrefetch : \(indexPaths)") } }
Swift
복사
//MyTableViewCell.swift import UIKit class MyTableViewCell: UITableViewCell { @IBOutlet weak var index: UILabel! override func awakeFromNib() { super.awakeFromNib() // Initialization code print("\n ----------- awake from nib ------------\n") } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) // Configure the view for the selected state } override func prepareForReuse() { super.prepareForReuse() print("\n ----------- prepareForReuse ------------\n") } deinit { print("\n ----------- deinit ------------\n") } }
Swift
복사

테이블 뷰 처음 실행시

처음 실행화면
----------- awake from nib ------------ cellForRowAt: 0 cell 0 : will display ----------- awake from nib ------------ cellForRowAt: 1 cell 1 : will display ----------- awake from nib ------------ cellForRowAt: 2 cell 2 : will display ----------- awake from nib ------------ cellForRowAt: 3 cell 3 : will display ----------- awake from nib ------------ cellForRowAt: 4 cell 4 : will display ----------- awake from nib ------------ cellForRowAt: 5 cell 5 : will display ----------- awake from nib ------------ cellForRowAt: 6 cell 6 : will display ----------- awake from nib ------------ cellForRowAt: 7 cell 7 : will display ----------- awake from nib ------------ cellForRowAt: 8 cell 8 : will display ----------- awake from nib ------------ cellForRowAt: 9 cell 9 : will display ----------- awake from nib ------------ cellForRowAt: 10 cell 10 : will display ----------- awake from nib ------------ cellForRowAt: 11 cell 11 : will display ----------- awake from nib ------------ cellForRowAt: 12 cell 12 : will display ----------- awake from nib ------------ cellForRowAt: 13 cell 13 : will display ----------- awake from nib ------------ cellForRowAt: 14 cell 14 : will display ----------- awake from nib ------------ cellForRowAt: 15 cell 15 : will display ----------- awake from nib ------------ cellForRowAt: 16 cell 16 : will display ----------- awake from nib ------------ cellForRowAt: 17 cell 17 : will display
Swift
복사
1.
awakeFromNib - tableViewCell 프로토콜
테이블 뷰 셀은 뷰컨트롤러를 상속받은 뷰가 아니기 때문에 viewDidload 메서드가 없어요!
대신 awakeFromNib() 메서드가 존재하며
viewDidload 와 비슷하게 초기화 작업을 이 메서드 내부에 구현해 주시면됩니다 ㅎ
viewDidload 와 마찬가지로 딱 한번만 호출됩니다. 이후 부터는 재사용에 대한 메서드가 호출됩니다 ( prepareForReuse )
2.
cellForRowAt - UITableViewDataSource 프로토콜
특정 위치에 삽입할 cell 에 대해 Data Source 를 요청하는 메서드
3.
willDisplay - UITableViewDelegate 프로토콜
셀이 우리눈에 나타나기 직전에 호출됩니다
정리하자면 처음 테이블 뷰 화면에 진입시
awakeFromNib > cellForRowAt > willDisplay 순으로 호출됩니다!

스크롤 시작시

----------- awake from nib ------------ cellForRowAt: 18 cell 18 : will display prefetch : [[0, 19], [0, 20], [0, 21], [0, 22], [0, 23], [0, 24], [0, 25], [0, 26], [0, 27], [0, 28]] ----------- awake from nib ------------ cellForRowAt: 19 cell 0 : didEndDisplaying cell 19 : will display prefetch : [[0, 29]] ----------- awake from nib ------------ cellForRowAt: 20 cell 1 : didEndDisplaying cell 20 : will display prefetch : [[0, 30]] ----------- prepareForReuse ------------ cellForRowAt: 21 cell 2 : didEndDisplaying cell 21 : will display prefetch : [[0, 31]] ----------- prepareForReuse ------------ cellForRowAt: 22 cell 3 : didEndDisplaying cell 22 : will display prefetch : [[0, 32]] ----------- prepareForReuse ------------ cellForRowAt: 23
Swift
복사
1.
prefetchRowsAt - UITableViewDataSourcePrefetching 프로토콜
iOS 10 부터 이용가능한 프로토콜의 메서드로 셀이 디스플레이에 보여지는 셀 이외의 셀의 정보를 미리 호출하여 데이터를 받아올 수 있다.
스크롤을 시작한 방향으로 10개의 셀에 대한 데이터를 미리 받아옵니다!
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) { print("prefetch : \(indexPaths)") }
Swift
복사
주로 GCD 나 Operation 으로 비동기 처리 작업을 명시합니다.
10개의 셀을 미리 받아옵니다
2.
didEndDisplay - UITableViewDelegate 프로토콜
셀이 화면에서 사라지면 호출되는 메서드
3.
prepareForReuse - UITableViewCell 프로토콜
재사용 가능한 셀을 준비하는 메서드
awakeFromNib 으로 화면에 보이는 만큼만 셀의 갯수를 생성했기 때문에
셀을 계속 생성하는 것이아닌 재사용합니다...!
재사용 된 셀을 사용할 때는 awakeFromNib 이 아니라 prepareForReuse 가 호출되는 것이지요 !
여기서 컨텐츠와 관련이 없는 셀의 속성을 재설정 해주면 됩니다!!!
단, 성능상의 이유로 너무 많은 로직을 구성하면 스크롤이 부자연스러워질 수 있습니다,,,
(재사용해야할 셀이 너무 늦게 구성되면 아무래도 ...)
override func prepareForReuse() { super.prepareForReuse() }
Swift
복사
정리하자면 스크롤을 시작하면 아래의 순서로 메서드가 호출됩니다.
prefetchRowsAt 스크롤 방향으로 10개의 셀 미리 받아옴
화면에서 사라지는 셀
didEndDisplay > prepareForReuse >
화면에 나타나는 셀
cellForRowAt > willDisplay >

반대로 스크롤시

1.
cancelPrefetch - UITableViewDataSourcePrefetching
스크롤 방향이 바뀜에 따라 prefetching 중이었던 데이터들이 필요없어질 수 있습니다.
따라서 이러한 셀들에 대해 prefetching 작업을 취소하는 메서드입니다.
불필요한 작업을 줄이니 아무래도 CPU 성능개선에 도움이 되겠죠?
2.
prefetchRowsAt
다시 스크롤 방향으로 10개의 셀을 미리 받아옵니다!
오늘은 cell 의 life Cycle 에 대해 심도있게 알아보았습니다!