Swift get & set , didSet & willSet

Swift get & set , didSet & willSet

2018, Oct 07    

*Property get, set, didSet, willSet

- property에는 {get, set} 또는 {didSet, willSet} 을 사용 가능

- 하나의 property에 {get, set}, {didSet, willSet} 모두 같이 쓸 순 없다

  • get, set sytax

class GetSet {

    var storage: Int		// get,set을 사용하기 위해선
   							// 또 다른 storage property가 필요
    init(storage: Int) {
        self.storage = storage
    }


    var intProperty: Int {		// get, set property
        get {					// get만 사용할 땐 get 생략 가능
            return storage - 1	// set만 사용은 불가
        }
        set {
            storage = newValue + 1
        }
    }

    func subscription() {
        print("get is \(intProperty)")
    }	// 읽기 전용인 get property를 사용해 데이터를 읽어온다						
}

let getSet = GetSet(storage: 15)
getSet.subscription()
// "get is 14"
// get은 GetSet 클래스를 인스턴스화 시킬 때 설정해준 초기화 값만 읽어온다.

getSet.intProperty = 16
getSet.storage
// 17

  • get, set을 사용하는 경우

1. property에 값이 할당 될 때 적절한 값인지 검증할 때

2. 다른 property 값에 의존적인 property를 관리 할 때

3. property를 private하게 사용하기 위해 참조


1. property에 값이 할당 될 때 적절한 값인지 검증할 때

- set의 newValue를 사용하여 property의 값이 바뀌기 전에 확인 가능
class Company {
    var memberStorage: Int = 0
    var member: Int {
        get {
            return memberStorage
        }
        set {
            if newValue > 0 {
                memberStorage = newValue
            } else {
                print("직원 수는 0명 보다 많아야 합니다.")
            }
        }
    }
}
let company = Company()
company.member = -5		// "직원 수는 0명 보다 많아야 합니다."
company.memberStorage	// 0

company.member = 15
company.memberStorage	// 15


2. 다른 property 값에 의존적인 property를 관리 할 때

- 회사 멤버 저녁식사 비용은 회사 멤버수에 비례(의존적)
- get을 활용하여 member 수에 맞춰 memberDinnerCost 계산 가능
- get만 사용하였기 때문에 클래스 외부에서 memberDinnerCost는 변경 불가, 오직 member 수에 따라 계산된 값이 반환된다.
class Company {
    var memberStorage: Int = 0
    var member: Int {
        get {
            return memberStorage
        }
        set {
            if newValue > 0 {
                memberStorage = newValue
            } else {
                print("직원 수는 0명 보다 커야 합니다.")
            }
        }
    }

    var memberDinnerCost: Int {
        get {
            return memberStorage * 10000
        }
    }

}
let dinnerCost = Company()
dinnerCost.member = 15
dinnerCost.memberDinnerCost		// 150000

  • didSet, willSet

- property observer : 내부적으로 “초기화 된” property 값을 감시

  • didSet : property 값이 변경된 직후에 호출

  • willSet : property 값이 변경되기 직전에 호출

- didSet, willSet을 활용하여 원하는 타이밍에 작업 수행 가능

* property observer를 사용하기 위해서는 property의 값이 ‘‘반드시 초기화’’ 되어 있어야 한다.

- 클래스의 init() {} 안에서 값을 할당 할 때는 didSet, willSet은 호출 되지 않음. 초기화 이후부터 property를 감시

var propertyObserver: Int = 10 {
    didSet(oldValue) {
        // propertyObserver의 값이 변경된 직후에 호출, oldValue는 propertyObserver의 변경 전 값
    }
    willSet(newValue) {
        // propertyObserver의 값이 변경되기 직전에 호출, newValue는 변경 될 새로운 값
    }
}

  • didSet, willSet(property observer) 사용하는 경우

- 가장 빈번한 사용은 Model에서 갱신된 값을 View에 보여줄 때

MVC

- ex) View에 점수를 표시하는 Label이 있다고 가정. 점수가 바뀔 때마다 View의 Label을 업데이트 해주고 싶을 때.
score = 85
scoreLabel.text = "Score: \(score)"
- 이렇게 해도 View의 Label은 정상적으로 바뀌지만, 만약 여러곳에서 score의 값을 바꾼다면 score의 값이 바뀌는 곳 마다 scoreLabel.text = “Score: (score)” 를 적어주어야 한다. (Bad Case)

- 이럴 때 property observer를 사용할 수 있다.

- property observer를 사용하면 score값이 바뀔 때마다 View의 값을 갱신하는 작업을 따로 해줄 필요가 없다.
var score: Int = 0 {
    didSet {
        scoreLabel.text = "Score: \(score)"
// score의 값이 바뀐 직후 didSet이 실행되어 scoreLabel.text의 값을 바꾼다
    }
}

- 바뀐 값과 바뀌기 전의 값을 비교하는 작업도 가능

var score: Int = 0 {
   didSet {
      print("현재 점수는: \(score), 이전 점수는: \(oldValue)")
   }
}
score = 5
// "현재 점수는: 5, 이전 점수는: 0"