개발콩블로그

[Swift] 프로퍼티 옵저버 (Property Observer) 본문

swift

[Swift] 프로퍼티 옵저버 (Property Observer)

devBean 2025. 1. 10. 15:50

안녕하세요!

개발콩입니다. 오늘은 프로퍼티 옵저버(Property Observer)에 대해서 알아보도록 하겠습니다!!

 

 

프로퍼티 옵저버 (Property Observer)

  • 프로퍼티의 값의 변화를 관찰하고  프로퍼티 값의 변화에 대응합니다.
  • 프로퍼티의 현재 값과 같은 값을 저장해도 호출됩니다.

 

Apple에서 제공하는 예시코드를 한번 볼까요?

var totalSteps: Int = 200 {
    willSet(newTotalSteps) {
        print("About to set totalSteps to \(newTotalSteps)")
    }
    didSet {
        if totalSteps > oldValue  {
            print("Added \(totalSteps - oldValue) steps")
        }
    }
}

totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps

 

위의 코드에서 프로퍼티 옵저버는 저장 프로퍼티에서 사용되고 있습니다.

해당 프로퍼티의 값을 변경했을 때

willSet구문에서 지정한 pinrt문이 먼저 호출이 되고, 이후 didSet구문이 호출되는 것을 볼 수 있습니다.

 

willSet

  • 값이 저장되기 직전에 호출됩니다.
  • 파라미터의 이름을 지정하지 않았다면, newValue를 통해 사용할 수 있습니다.

 

didSet

  • 새로운 값이 저장된 직후에 호출됩니다.
  • 파라미터의 이름을 지정하지 않았다면, oldValue를 통해 사용할 수 있습니다.

 

 

프로퍼티 옵저버를 사용할 수 있는 공간

  • 저장 프로퍼티
  • 상속받은 저장 프로퍼티
  • 상속받은 연산 프로퍼티

 

저장 프로퍼티와 상속받은 저장 프로퍼티의 경우 위의 예시코드와 비슷하여 이해하기 쉬웠습니다.

하지만, 프로퍼티 옵저버를 상속받은 연산 프로퍼티에도 적용할 수 있다는 것은 놓치지 쉬운 사실인 것 같습니다.

 

 

상속받은 연산 프로퍼티에서의 프로퍼티 옵저버

상속받은 연산 프로퍼티에서도 프로퍼티 옵저버를 활용할 수 있습니다.

 

class Plant {
    var name: String = "flower"
    
    var nameDescription: String {
        get {
            print("get")
            return name
        } set {
            print("set - \(newValue)")
            name = newValue
        }
    }
}

class Bean: Plant {
    override var nameDescription: String {
        willSet {
            print("willSet - \(newValue)")
        } didSet {
            print("didSet - \(oldValue)")
        }
    }
}

let bean = Bean()
bean.nameDescription = "bean"

// get
// willSet - bean
// set - bean
// didSet - flower

 

저는 지금 bean.nameDescription에 값을 쓰고 있는 상황입니다.

get - willSet - set - didSet 순서로 잘 진행되는 것을 확인할 수 있습니다.

 

위의 코드에서는 didSet 구문에서 nameDescription을 변경하게 된다면,

무한 재귀가 발생하게 됩니다.

get - willSet - set - didSet -> get - willSet...

당연한 결과입니다.

 

 

 

그렇다면, 저장 프로퍼티에서의 무한재귀는 발생할까요?

프로퍼티 옵저버의 무한재귀

 

var counter: Int = 0 {
    didSet {
        counter += 1
        print(counter)
    }
}

counter = 0
// 1

 

다른 외국 블로그에는 아래의 코드를 무한재귀가 발생하는 코드로 소개하고 있습니다.

실제로 그려보면, 저장된 이후 -> 다시 값을 저장 (값 변경됨) -> 다시 + 1 -> 저장된 이후 ....

하지만 실제로 무한재귀가 발생하지 않습니다.

 

 

하지만 왜 didSet만 사용할 때는 무한재귀가 발생하지 않을까요?

 

해당 자료에 대해서 많은 자료를 찾아보았고, Apple 개발자 포럼에서 힌트를 얻을 수 있었습니다.

https://forums.developer.apple.com/forums/thread/105784

 

👉 Swift 5.0이 되면서 willSet, didSet에서 본인을 수정하는 것은 트리거 되지 않는다.

 

 

 

제 결론 및 느낀점

프로퍼티 옵저버는 저장 프로퍼티에서만 사용할 수 있을 것 같다는 편견을 없앨 수 있었던 것 같습니다.

 

연산 프로퍼티에서는 무한 재귀가 발생할 수 있어서 사용을 조심해야하지만,

프로퍼티 옵저버의 경우 생각과는 다르게 무한 재귀가 발생하지 않음을 알 수 있었습니다.

 

프로퍼티 옵저버를 공부하게 되면서 특정한 동작이 반복이 되는데,

이러한 동작의 시작이 프로퍼티의 값의 변경일 경우에 프로퍼티 옵저버를 잘 활용할 수 있을 것 같습니다.

 

 

 

 

참고

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/properties/#Property-Observers

https://forums.developer.apple.com/forums/thread/105784

'swift' 카테고리의 다른 글

[Swift] GCD - 1 동시성 프로그래밍  (3) 2025.01.25
[Swift] 싱글톤 패턴 (Singleton Pattern)  (0) 2025.01.15
[Swift] 연산 프로퍼티(Computed Property)  (2) 2025.01.09
[Swift] Generic  (1) 2025.01.05
[Swift] Property Wrapper  (0) 2025.01.03