티스토리 뷰

Tistory에는 요약본이 올라 와 있습니다.
Syntax Highlighting으로 편하게 읽고 싶으신 분들은 여기서 읽으시면 되어유~
[노션 바로가기]

문제 상황

각 cell마다 Image와 Like Button이 존재한다. 버튼을 눌러 상태 변화를 하며 모델을 변경시키고 그에 맞게 뷰에서도 Background Color, Text를 변경해준다. 그러나 상태 변경 이후 스크롤을 하면 cell이 재사용되면서 다른 사진임에도 불구하고 버튼은 여전히 빨간색인 것이 문제다.

원래는 이렇게 하지 않았던가?

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
	if let cell = tableView.dequeueReusableCell(withidentifier: HomeCell.identifier, for: indexPath) as? HomeCell {
			cell.likeButton.rx.tap
					.subscribe(onNext: { _ in 
							updateModel()
					})
					.disposed(by: disposeBag)
			return cell
	}
	return UITableViewCell()
}

 

cellForRowAt 에서 알맞은 cell을 dequeue 하여 data source에서 알맞은 data를 넣어주는. 전혀 문제가 될 수 없는 코드. 안희 그런데 자꾸 like button이 말썽을 부렸다. 난 두 번째 사진에 좋아요 눌렀는데 왜 다섯번째 너도 좋아요 누른 것 처럼 뜨냐구 널 좋아한 적은 없어뗘..(?👀)

시도 1 (은 사실 머릿속의 시도)

사실 이 시도 자체가 무의미 하다는 것을 알지만 머릿속으로 시뮬레이션을 해보았지. 다양한 종류의 custom cell을 만들어서 사용하는 경우 종종 layout이 이전 cell의 영향을 받곤 했다. prepareForReuse, setNeedsLayout, layoutIfNeeded를 자주 썼었지만 엄밀히 말하면,

  1. 원래 prepareForReuse는 view의 content를 초기화하는 용도로 쓰는게 아님
  2. 이번에는 layout 문제가 아니기 때문에 setNeedsLayout, layoutIfNeeded 와는 무관한 문제

prepareForReuse의 쓰임

tableView(:cellForRowAt:)는 원칙적으로 cell을 재사용할 때 cell의 content를 모두 초기화 시킨다. 그래서 성능적 이유로 prepareForReuse 내부에서는 content와 무관한 것들을 초기화 시켜야 한다. ex. alpha 값, selection state, editing 등

1차 결론 : Rx의 뭔가가 원인이다 (feat. DisposeBag가 정확히 뭐지...)

결론을 내렸지만 더 큰 일이 되어버린 셈이었다. 시작한지 2주 된 Rx가 문제라니... 나 울어😭 코드를 찬찬히 살펴보는데 문득 DisposeBag에 대한 이해가 부족하다는 것을 알았다. RxCocoa나 Subjects/Relays는 그나마 알고 붙인 애들인데 .dispose(by: disposeBag)는 거의 뭐 습관. 제 2의 guard let self = self else { return } 느낌 (다덜 뭔 너낌인지 알쥐? 찡긋 >_-;;)

시도 2 : DisposeBag 알아보기 [글 바로가기]

2차 결론 : DisposeBag가 원인이 맞다

cell은 재사용이 되기 때문에 화면 밖에서 사라진다고 deinit 되는 것이 아니다.

그렇기 때문에 기존에 유지하고 있던 subscription이 취소가 되지 않고 유지되면서 문제가 생긴 것이다.

방금 이 두 문장으로 우리가 원하는 바가 분명해진 것 같다.

⇒ 화면 밖에서 사라질 때 subscription을 dispose 하기.

 

DisposeBag 글에서도 썼듯이 deinit 시점까지 기다릴 수 없으니(오지 않으니까) 기존에 갖고 있던 dispose bag를 다른 것으로 갈아끼던지, 새로 만들어주면 된다. 아래와 코드와 같이.

func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
	guard let cell = tableView.dequeueReusableCell(withIdentifier: HomeCell.identifier, for indexPath) as? HomeCell else { return }
	cell.disposeBag = DisposeBag()
}

오늘도 무사해결!

 

'iOS' 카테고리의 다른 글

[iOS] NotificationCenter는 중앙우체국  (0) 2021.07.27
[iOS] Update Cycle: 뷰 업데이트 메소드  (0) 2021.07.11
[Rx] DisposeBag  (0) 2021.05.17
[Rx] UITableViewCell 밀어서 삭제  (0) 2021.05.13
[Swift] Struct와 Class 비교하기  (0) 2021.05.06
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함