[Swift] class와 struct에서 didSet의 차이

didSet 그거 간단한거 아니었어요?! (오열)

naljin
4 min readJul 12, 2020

왜 이 글을 쓰게 되었는가

며칠전에 이런 코드를 봤따.

@IBOutlet weak var button: UIButton! {
didSet {
button.backgroundColor = .red
}
}

didSet 에 해당 Outlet의 초기 설정을 몰아넣는 것!

“그러면 button의 속성이 바뀔때 마다 didSet 이 불리는거 아녜요? ㅇㅂㅇ?” 라고 질문했더니

“프로퍼티라면 그렇게 동작하겠지만 IBOutletclass라 저 button 자체를 바꾸지 않는 이상 didSet이 호출되지 않아요. ” 라고 답변해주셨ㄷㅏ.

ㅇㅖ? ㅠ… 방과 후 블로그 시작합니다..

깨달음의 여정…🚗

class Foo {
var bar = 1
}

var test: Foo = Foo() {
didSet {
print("변경사항 있다!")
}
}

test.bar = 1 // 아무일도 일어나지 않는다

아니 나는 이러면 당연히 “변경사항 있다”가 프린트 될줄 알았지 ㅇㅅㅇ 근데 아무일도 일어나지 않는다니..! 이게 어찌된 일이요 didSet 양반!

여윽시 구글에는 비슷한 질문이 나와있었다. 답변을 요약해보면 아래와 같다.

didSet이 reference type (ex. class)에 달려있을 때

  • didSet observer가 달려있는 애(test)는 class 인스턴스로 reference type임.
  • reference type의 instance는 제자리에서 변할 수 있음 (in place)
  • 즉, test자체를 바꾼게 아니라 test.number을 바꿨기 때문에 아무일도 일어나지 않음
  • test = Foo() 이렇게 하면 didSet 동작

didSet이 value type (ex. struct)에 달려있을 때

  • value type은 비록 그렇게 보일지라도 실제로는 제자리에서 변할 수 없음 (not mutable in place)
  • value type의 instance를 변경했다고 하면, 다른 instance로 원래 instance를 변경하는 것임
  • test.bar = 1 이어도 인스턴스가 변경되는 것이기 때문에 didSet 동작

오키오키 IBOutletclass 라 reference type 이기 때문에 해당 인스턴스 자체를 갈아 끼우지 않는 이상 didSet이 동작하지 않는거였군!

마무리

사실 classstruct의 이해의 부족에서 나온 문제였나? 싶기도 하다 ㅋㅎ 지금이라도 좀 더 알았으면 됐지 모~

그러면… 안돼…?

보너스 — Outlet의 didSet 은 언제 trigger 되는가

  • Outlet 프로퍼티는 class가 막 초기화 될 때는 nil 상태
  • nib으로부터 object가 초기화 될 때에 값을 갖게 됨
  • 모든 Outlet 이 nil이 아님을 확신할 수 있을때는 viewDidLoad
  • 따라서 Outlet에 있는 didSet 옵저버는 viewDidLoad 바로 전에 호출
  • 따라서 Outlet 프로퍼티 다룰 때 조심해야 함. 예를들어 prepareForeSegue때 접근하려고 하면 nil 받게 될 것

참고

--

--