티스토리 뷰
프로퍼티 옵저버 (Property Observers)
프로퍼티 옵저버는 프로퍼티의 값이 변경될 때 감지하고, 반응 합니다.
매번 값이 set(설정) 될때 불려지며, 새로운 값이 현재 값과 같더라도 불려집니다.
아래 위치에 프로퍼티 옵저버를 추가할 수 있습니다.
- 저장 프로퍼티를 정의할 때
- 저장 프로퍼티를 상속할 때
- 연산 프로퍼티를 상속할 때
서브클래스의 프로퍼티에 옵저버를 정의할 수 있습니다.
연산 프로퍼티는 setter의 값 변화를 감지할 수 있기 때문에 옵저버를 정의할 필요가 없습니다.
프로퍼티에서 아래 옵저버를 하나 또는 둘다 사용할 수 있습니다.
- willSet: 값이 저장되기 전에 호출됩니다.
- didSet: 새로운 값이 저장된 후에 바로 호출 됩니다.
willSet
willSet 에서, 새 프로퍼티 값의 파라미터명을 아래와 같이 지정할 수 있는데 (newTotalSteps),
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) { // newTotalSteps로 파라미터명 지정
print("About to set totalSteps to \(newTotalSteps)")
}
}
}
지정하지 않으면 기본값 newValue로 사용할 수 있습니다.
class StepCounter {
var totalSteps: Int = 0 {
willSet { // 파라미터명을 지정하지 않아 newValue로 사용
print("About to set totalSteps to \(newValue)")
}
}
}
didSet
didSet 에서도 비슷하게, 파라미터 이름을 지정하지 않으면, oldValue로 사용할 수 있습니다.
파라미터명 지정
class StepCounter {
var totalSteps: Int = 0 {
didSet(oldTotalSteps) { // oldTotalSteps로 파라미터명 지정
if totalSteps > oldTotalSteps {
print("Added \(totalSteps - oldTotalSteps) steps")
}
}
}
}
파라미터명 X
class StepCounter {
var totalSteps: Int = 0 {
didSet { // 파라미터명 지정하지 않아 기본값 oldValue로 사용
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
}
아래는 willSet과 didSet을 둘다 사용한 예제 입니다.
값이 변하기 전과 변하고 나서 호출되는 순서를 확인할 수 있습니다.
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
print("oldValue:", oldValue)
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// oldValue: 0
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// oldValue: 200
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// oldValue: 360
NOTE
만약 in-out 파라미터로 선언된 함수의 인자에 프로퍼티를 넘기면 willSet과 didSet이 항상 실행됩니다.
in-out 파라미터는 프로퍼티가 항상 복사 되기 때문에 원래 값에 새 값을 덮어 쓰게 됩니다.
전역변수와 지역변수 (Global and Local Variables)
연산 프로퍼티와 프로퍼티 옵저버 기능은 전역변수와 지역변수 모두에서 사용 가능합니다.
전역 변수 - 메소드, 클로저, 타입 컨텍스트 외부에 정의된 변수
지역 변수 - 메소드, 클로저, 타입 컨텍스트 안에 정의된 변수
NOTE
전역 상수/ 변수는 Lazy Stored Properties 와 같이 지연 연산 됩니다.
하지만 lazy 키워드가 필요 없습니다.
반면, 지역 상수/ 변수는 지연 연산될 수 없습니다.
타입 프로퍼티 (Type Properties)
여기에 정리 했으나, 공식문서를 보고 한번더 정리하겠습니다.
타입프로퍼티는 특정 타입에 속한 프로퍼티로 그 타입에 해당하는 단 하나의 프로퍼티만 생성됩니다.
모든 인스턴스에 공통으로 사용되는 값을 정의할때 유용합니다.
NOTE
타입프로퍼티는 항상 초기값을 지정해서 사용해야 합니다.
타입 자체에는 초기자(Initializer)가 없어 초기화 할 곳이 없기 때문!!
타입 프로퍼티 구문 (Type Property Syntax)
타입프로퍼티를 정의 할때 static 키워드를 사용합니다.
클래스 타입에 대한 연산 타입 프로퍼티의 경우 class 키워드를 사용하여 하위클래스가 상위클래스의 구현을 재정의 (override)할 수 있습니다.
아래는 구조체, 열거형, 클래스의 타입프로퍼티에 대한 구문을 보여주는 예 입니다.
struct SomeStructure {
static var storedTypeProperty = "Some value." // 저장 타입프로퍼티
static var computedTypeProperty: Int { // 연산 타입프로퍼티
return 1
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value." // 저장 타입프로퍼티
static var computedTypeProperty: Int { // 연산 타입프로퍼티
return 6
}
}
class SomeClass {
static var storedTypeProperty = "Some value." // 저장 타입프로퍼티
static var computedTypeProperty: Int { // 연산 타입프로퍼티
return 27
}
class var overrideableComputedTypeProperty: Int { // 재정의 가능한 연산타입프로퍼티
return 107
}
}
NOTE
위의 예제처럼 연산 프로퍼티는 읽기 전용으로 사용할 수도 있지만,
읽고 쓰는 프로퍼티로도 사용할 수 있습니다.
타입 프로퍼티의 접근과 설정 (Querying and Setting Type Properties)
타입프로퍼티도 점 구문(.)으로 프로퍼티 값을 할당하거나, 가져올 수 있습니다.
print(SomeStructure.storedTypeProperty) // Some value
SomeStructure.storedTypeProperty = "Another value"
print(SomeStructure.storedTypeProperty) // Another value
print(SomeEnumeration.computedTypeProperty) // 6
print(SomeClass.overrideableComputedTypeProperty) // 107
아래는 오디오 채널 볼륨을 조절하고 관리하는 구조체로 두개의 저장 타입프로퍼티를 사용한 예 입니다.
- 오디오 채널의 레벨이 0인 경우 표시등은 켜지지 않습니다.
- 오디오 채널의 레벨이 10인 경우 모든 표시등이 켜집니다.
- 위 그림에서 왼쪽 오디오 채널 레벨은 9, 오른쪽 채널 레벨은 7 입니다.
- 아래는 채널 레벨이 10을 초과하지 않게 제한하고,
10이하의 레벨이면 해당 값을 할당해주는 예제 입니다.
struct AudioChannel {
static let maxLevel = 10
static var inputLevelForAllChannels = 0
var currentLevel: Int = 0 {
didSet {
if currentLevel > AudioChannel.maxLevel {
currentLevel = AudioChannel.maxLevel
}
if currentLevel > AudioChannel.inputLevelForAllChannels {
AudioChannel.inputLevelForAllChannels = currentLevel
}
}
}
}
var leftChannel = AudioChannel() // 왼쪽 채널
var rightChannel = AudioChannel() // 오른쪽 채널
// 1
leftChannel.currentLevel = 7
print(leftChannel.currentLevel) // 7
print(AudioChannel.inputLevelForAllChannels) // 7
// 2
rightChannel.currentLevel = 300
print(rightChannel.currentLevel) // 10
print(AudioChannel.inputLevelForAllChannels) // 10
코드 풀이
1.
- leftChannel의 currentLevel에 7을 할당 합니다.
- didSet 부분에 첫번째 조건문에 만족하지 않으므로 통과
if currentLevel (7) > AudioChannel.maxLevel (10) { // false
currentLevel = AudioChannel.maxLevel
}
- 두번째 조건문에 만족
if currentLevel (7) > AudioChannel.inputLevelForAllChannels (10) {
AudioChannel.inputLevelForAllChannels = currentLevel
// AudioChannel.inputLevelForAllChannels = 7
}
2.
- rightChannel의 currentLevel에 300을 할당 합니다.
- didSet 부분에 첫번째 조건문에 만족
if currentLevel (300) > AudioChannel.maxLevel (10) {
currentLevel = AudioChannel.maxLevel
// currentLevel = 10
}
다음은
Property Wrapper에 대해 블로깅 해보겠습니다!
'iOS' 카테고리의 다른 글
[iOS] async,sync 와 serial, concurrent의 차이? (0) | 2022.03.13 |
---|---|
[iOS] 네비게이션바 뒤로가기 버튼 커스텀 - navigationBar back button custom (0) | 2022.03.11 |
[Swift] 프로퍼티 - Property Wrapper (3/3) (0) | 2022.03.04 |
[Swift] 프로퍼티 (Property) (1/3) (0) | 2022.03.02 |
[iOS] 빈화면 터치시 TextField 키보드 내리기 (0) | 2022.02.27 |
- Total
- Today
- Yesterday
- swift protocol
- Swift Error Handling
- Swift Leetcode
- 2023년 회고
- Class
- CS 네트워크
- Swift inout
- Swift 프로그래머스
- Swift final
- 원티드 프리온보딩
- Swift 알고리즘
- RTCCameraVideoCapturer
- Swift init
- Swift
- Swift ModernRIBs
- Swift RIBs
- swift reduce
- Swift 프로퍼티
- removeLast()
- Swift joined()
- swift programmers
- swift 고차함수
- Combine: Asynchronous Programming with Swift
- Swift joined
- iOS error
- swift property
- Swift 내림차순
- RIBs tutorial
- ios
- swift (programmers)
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |