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 에 대해 심도있게 알아보았습니다!