안녕하세요 Ruel입니다.🎶
Property Observer에 대해서 아시나요?
Property Observer는 값의 변화를 감지하여 그에 따라 적절한 동작을 수행할 수 있게 해 줘요
이를 통해서 코드를 명확하게 작성할 수 있고, 유지보수가 쉬워진다고 생각해요
재밌는 Property Observer 한번 알아보도록 합시다.
Property Observer
앞서 간단하게 설명한 것처럼 Property Observer는 특정 프로퍼티의 값이 변경될 때 감지하고 이때 원하는 동작을 실행 시켜줘요.
음... 실생활에 예를 뭘로 들 수 있을까 고민해 봤는데...
누군가가 우리 집에 들어올 때마다 "누가 왔나??" 하면서 "확인하는 우리"라고 볼 수 있을까 해요
Property Observer는 같은 값으로 변경되더라도 동작해요
마치 집에 있던 누군가가 택배를 받으러 잠깐 나갔다가 들어와도 다시 확인한다고 할까??
❗️그러나 init() 메서드를 통해서 초기화할 때는 동작하지 않아요!
Property Observer에는 'willSet', 'didSet' 두 가지가 존재해요
- willSet: 프로퍼티의 값이 변경되기 직전에 호출돼요. 새로운 값으로 바뀌기 전 "이제 곧 바뀌겠군!"이라고 알려주는 역할을 해요
- 새로운 값은 'newValue'라는 기본 매개 변수로 제공돼요
- didSet: 프로퍼티의 값이 변경된 직후 호출돼요. "이제 바뀌었군!" 하고 알려주는 역할이죠.
- 이전 값은 'oldValue'라는 기본 매개 변수로 제공돼요
var propertyObserver: Int = 20 {
willSet {
// newValue 제공
}
didSet {
// oldValue 제공
}
}
Property Observer는 프로퍼티의 값이 변해 View를 업데이트할 때 주로 사용된다고 생각해요.
예를 들면 점수가 바뀔 때마다 뷰를 업데이트하는??
var count: Int = 0 {
didSet {
// 변경된 직후 호출되니까
myPoint.text = "\(count)점 입니다."
}
}
var count: Int = 0 {
didSet(oldCount) {
// 변경된 직후 호출되니까
myPoint.text = "이전 점수는 \(oldCount)점 입니다."
}
}
var count: Int = 0 {
didSet {
// 변경된 직후 호출되니까
myPoint.text = "\(oldValue)점에서 \(count)점으로 변경되었습니다."
}
}
온도계로 예로 들어볼게요
import Foundation
class Thermometer {
var temperature: Double {
willSet(newTemperature) {
print("Temperature will change to \(newTemperature)°C")
}
didSet(oldTemperature) {
print("Temperature did change from \(oldTemperature)°C to \(temperature)°C")
}
}
init(temperature: Double) {
self.temperature = temperature
}
}
let thermometer = Thermometer(temperature: 25.0)
thermometer.temperature = 30.0
thermometer.temperature = 20.0
Property Observer
- willSet(newTemperature): temperature의 값이 새로운 값으로 변경되기 직전 호출
- newTemperature: 새로운 값
- didSet(oldTemperature): temperature의 값이 새로운 값으로 변경된 후 호출
- oldTemperature: 이전 값
실행 결과를 보면 온도계가 스스로 "곧 더워질 거야", "이제 좀 추워졌어"라고 하는 거 같아요.
Temperature will change to 30.0°C
Temperature did change from 25.0°C to 30.0°C
Temperature will change to 20.0°C
Temperature did change from 30.0°C to 20.0°C
Property Observer를 사용하면 값의 변화를 감지하고 때에 따라 적절한 동작을 실행시킬 수 있어요.
Property Observer를 안 쓰고는?
Property Observer를 안 쓰는 예로 한번 차이점 볼게요
1. getter와 setter 사용하기
import Foundation
class Thermometer {
private var _temperature: Double
var temperature: Double {
get {
return _temperature
}
set {
if _temperature != newValue {
print("Temperature will change to \(newValue)°C")
let oldTemperature = _temperature
_temperature = newValue
print("Temperature did change from \(oldTemperature)°C to \(temperature)°C")
}
}
}
init(temperature: Double) {
self._temperature = temperature
}
}
let thermometer = Thermometer(temperature: 25.0)
thermometer.temperature = 30.0
thermometer.temperature = 20.0
위처럼 Property Observer를 사용하지 않고 getter와 setter를 정의하여 값이 변경될 때마다 필요한 동작을 수행해요.
앞서 Property Observer를 사용한 코드보다 복잡하지 않나요??
복잡하고 길고.... 이렇게 사용하다 보면 실수할 것 같네요
2. RxSwift 사용하기
import Foundation
import RxSwift
class Thermometer {
private let disposeBag = DisposeBag()
private let temperatureSubject = PublishSubject<Double>()
var temperature: Double {
didSet {
temperatureSubject.onNext(temperature)
}
}
init(temperature: Double) {
self.temperature = temperature
temperatureSubject
.do(onNext: { newTemperature in
print("Temperature will change to \(newTemperature)°C")
})
.subscribe(onNext: { [weak self] newTemperature in
if let oldTemperature = self?.temperature {
print("Temperature did change from \(oldTemperature)°C to \(newTemperature)°C")
}
})
.disposed(by: disposeBag)
}
}
let thermometer = Thermometer(temperature: 25.0)
thermometer.temperature = 30.0
thermometer.temperature = 20.0
- PublishSubject를 사용하여 temperature 값의 변화를 스트림으로 처리할 수 있어요.
- PublishSubject를 사용하여 temperature 값의 변화를 감지하고 subscribe를 통해 새로운 값이 들어올 때마다 출력을 해요
3. Combine 사용해 보기
import Foundation
import Combine
class Thermometer {
private var cancellables = Set<AnyCancellable>()
private let temperatureSubject = CurrentValueSubject<Double, Never>(0.0)
var temperature: Double {
didSet {
temperatureSubject.send(temperature)
}
}
init(temperature: Double) {
self.temperature = temperature
temperatureSubject
.handleEvents(receiveOutput: { newTemperature in
print("Temperature will change to \(newTemperature)°C")
})
.sink(receiveValue: { [weak self] newTemperature in
if let oldTemperature = self?.temperature {
print("Temperature did change from \(oldTemperature)°C to \(newTemperature)°C")
}
})
.store(in: &cancellables)
}
}
let thermometer = Thermometer(temperature: 25.0)
thermometer.temperature = 30.0
thermometer.temperature = 20.0
Combine에서는 CurrentValueSubject를 사용하여 temperature의 값의 변화를 스트림으로 처리해요
CurrentValueSubject를 사용하여 temperature값의 변화를 감지하고 sink를 통해 새로운 값이 설정될 때마다 출력을 처리하죠
그리고 handleEvents를 사용해서 sink에 값을 받기 전에 이벤트를 처리할 수 도 있어요
🤔 흠... RxSwift, Combine을 쓰면 현재 코드에서는 더 복잡한 거 같아요.....
이런 방법들을 알고 상황에 맞게 사용해야겠죠~?
막무가내로 RxSwift와 Combine을 쓰다 보니 까먹는 상황이 생겼는데
이렇게 다시 보니 기억이 다시 떠오르네요
알고 있더라도 보고보고 또 봐야 할 거 같아요
잊어버리지 않게 😭
잘못된 부분 혹은 새롭게 알려 주실 부분 있으시면 언제든지 댓글 남겨주세요.💬
감사합니다.🙇🏻♂️
'iOS > Swift' 카테고리의 다른 글
Swift] Singleton pattern(싱글톤) (0) | 2024.08.11 |
---|---|
DI(Dependency Injection) - 의존성 주입 (0) | 2024.08.10 |