본문 바로가기
YAGOM CAREER STARTER

[TIL] 20230127: Type Properties

by Rhode 2023. 1. 28.

본문은 야곰 아카데미 커리어 스타터 캠프를 통해 학습한 내용을 회고한 글입니다.


Type Properties

인스턴스 프로퍼티는 특정 타입의 인스턴스에 속해있는 프로퍼티이다. 특정 타입의 인스턴스를 새로 만들 때마다, 다른 인스턴스와는 독립적인 그 자신만의 프로퍼티 값을 가지게 된다.

특정 타입의 인스턴스에 속하는 것이 아닌, 타입 그 자신에 속하는 프로퍼티를 정의할 수도 있다. 얼마나 많은 인스턴스를 만들든지와 상관 없이, 이러한 프로퍼티에 대해서 단 하나만의 사본이 존재하게 될 것이다. 이러한 종류의 프로퍼티를 타입 프로퍼티라고 부른다.

타입 프로퍼티는 모든 인스턴스가 사용할 수 있는 상수 프로퍼티나 특정 타입의 모든 인스턴스에서 보편적으로 값을 저장하는 변수 프로퍼티와 같이, 특정 타입의 모든 인스턴스에 보편적으로 사용되는 값을 정의하는 데에 있어서 유용하다. 

저장 타입 프로퍼티는 변수도 상수도 될 수 있다. 연산 타입 프로퍼티는 연산 인스턴스 프로퍼티가 그렇듯이 항상 변수 프로퍼티로서 명시된다. 

 

NOTE
저장 인스턴스 프로퍼티와 달리, 저장 타입 프로퍼티에는 항상 디폴트 값을 주어야한다. 타입 그 자체로는 저장 타입 프로퍼티의 초기화 시점에 값을 할당해줄 수 있는 이니셜라이저가 없기 때문이다. 
저장 타입 프로퍼티는 그 첫 접근에서 lazy하게 초기화 된다. 저장 타입 프로퍼티는 멀티플 쓰레드에 동시적으로 접근이 될 때에도 단 한 번만 초기화 되는 것이 보장되며, lazy 모디파이어에 의해서 mark될 필요 없다. 

 

Type Property Syntax

C나 Objective-C에서는 타입과 관련된 static 상수나 변수를 global static 변수로서 정의한다. 그런데, swift에서는 타입 프로퍼티가 타입의 바깥 중괄호 내에서 타입의 정의의 일부로서 쓰여진다. 그리고 각각의 타입 프로퍼티는 자신이 서포트하는 타입에 명시적으로 scoped된다.

타입 프로퍼티는 static 키워드를 사용해서 정의할 수 있다. 클래스 타입의 연산 타입 프로퍼티에 대해서는, 서브클래스가 슈퍼클래스의 구현을 override하는 것을 허용하기 위해서 class키워드를 사용할 수 있다. 다음은 저장 타입 프로퍼티와 연산 타입 프로퍼티의 문법을 보여주는 예시이다:

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)
// Prints "Some value."
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// Prints "Another value."
print(SomeEnumeration.computedTypeProperty)
// Prints "6"
print(SomeClass.computedTypeProperty)
// Prints "27"

다음의 예시는 두개의 저장 타입 프로퍼티를 오디오 채널의 음성 레벨을 구조화하는 구조체의 일부로서 사용한다. 각각의 채널은 0부터 10까지의 음성 레벨 integer를 갖고 있다.

아래의 그림은 어떻게 오디오 채널중 두 개가 모델 스테레오 음성 레벨로 합쳐질 수 있는지를 보여준다. 채널의 음성 레벨이 0일 때, 어떠한 채널의 불도 들어오지 않는다. 오디오 레벨이 10일때, 모든 채널의 불이 들어온다. 이 그림에서, 왼쪽의 채널은 9의 레벨을 가지고, 오른쪽의 채널은 7의 레벨을 가지고 있다:

오디오 채널은 AudioChannel 구조체의 인스턴스에 의해서 대표된다:

struct AudioChannel {
    static let thresholdLevel = 10
    static var maxInputLevelForAllChannels = 0
    var currentLevel: Int = 0 {
        didSet {
            if currentLevel > AudioChannel.thresholdLevel {
                // cap the new audio level to the threshold level
                currentLevel = AudioChannel.thresholdLevel
            }
            if currentLevel > AudioChannel.maxInputLevelForAllChannels {
                // store this as the new overall maximum input level
                AudioChannel.maxInputLevelForAllChannels = currentLevel
            }
        }
    }
}

AudioChannel 구조체는 그 기능을 서포트하기 위해서 두개의 저장 타입 프로퍼티를 정의한다. thresholdLevel은 음성 레벨이 가질 수 있는 최대의 한계 값을 정의한다. 이것은 모든 AudioChannel 인스턴스에서 10의 상수값을 가진다. 만약 음성 신호가 10보다 큰 값이 들어오게 되면, 이 한계 값에 의해서 한도가 정해질 것이다(아래에서 설명되었듯이).

두번째 타입 프로퍼티는 maxInputLevelForAllChannels라는 이름의 변수 저장 프로퍼티다. 이것은 모든 AudioChannel 인스턴스로부터 받는 최대 인풋 값을 트랙킹한다. 이것은 0에서 시작한다. 

AudioChannel 구조체는 currentLevel이라는 저장 인스턴스 프로퍼티도 정의하고 있는데, 이것은 채널의 현재 음성레벨을 0부터 10까지로 보여준다.

currentLevel 프로퍼티는 currentLevel의 값이 설정될 때마다 그것을 관측하기 위해서 didSet 프로퍼티 옵저버를 갖는다. 이 옵저버는 두가지 체크사항을 수행한다:

  • 만약 currentLevel의 새로운 값이 허용되는 thresholdLevel보다 크다면, 프로퍼티 옵저버는 currentLevel의 한도를 thresholdLevel로 정해준다.
  • 만약 currentLevel의 새로운값이 (한도를 정해준 후에) 어떠한 AudioChannel 인스턴스로부터 받은 이전의 값보다 높다면, 프로퍼티 옵저버가 새로운 currentLevel 값을 maxInputLevelForAllChannels 타입 프로퍼티에 저장해준다.
NOTE
두 가지 체크사항 중 첫번재에서, didSet 옵저버는 currentLevel을 다른 값으로 설정해준다. 그렇지 않다면, 옵저버가 다시 호출될 것이다.

스트레오 사운드 시스템의 음성 레벨을 나타내기 위해, leftChannel과 rightChannel이라는 새로운 두 개의 채널을 만들기 위해서 AudioChannel 구조체를 사용할 수 있다:

var leftChannel = AudioChannel()
var rightChannel = AudioChannel()

만약 왼쪽 채널의 currentLevel을 7로 설정한다면, maxInputLevelForAllChannels 타입 프로퍼티가 7로 업데이트 되는 것을 볼 수 있을 것이다.

leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
// Prints "7"
print(AudioChannel.maxInputLevelForAllChannels)
// Prints "7"

만약 오른쪽 채널의 currentLevel을 11으로 설정하고자 한다면, 오른쪽 채널의 currentLevel 프로퍼티가 최댓값인 10으로 한도 지어지는 것을 볼 수 있을 것이다. 그리고 maxInputLevelForAllChannels 타입 프로퍼티가 10으로 업데이트 되는 것을 볼 수 있을 것이다. 

rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
// Prints "10"
print(AudioChannel.maxInputLevelForAllChannels)
// Prints "10"

 

 

참조

https://docs.swift.org/swift-book/LanguageGuide/Properties.html#ID264

 

Properties — The Swift Programming Language (Swift 5.7)

Properties Properties associate values with a particular class, structure, or enumeration. Stored properties store constant and variable values as part of an instance, whereas computed properties calculate (rather than store) a value. Computed properties a

docs.swift.org

 

 

'YAGOM CAREER STARTER' 카테고리의 다른 글

[TIL] 20230131: -  (0) 2023.02.06
[TIL] 20230130: foreach, compactMap  (0) 2023.01.31
[TIL] 20230126: SOLID  (0) 2023.01.28
[TIL] 20230124: Generics  (0) 2023.01.26
[TIL] 20230123: unit test, TDD, filter, reduce  (0) 2023.01.23