🔥 타입 속성

774자
9분

인스턴스 속성은 특정 타입의 인스턴스에 속하는 속성입니다. 해당 타입의 새 인스턴스를 생성할 때마다 각 인스턴스는 다른 인스턴스와 구분되는 자체 속성 값 집합을 가지게 되죠.

하지만 타입 자체에 속하는 속성을 정의할 수도 있습니다. 이런 속성은 해당 타입의 인스턴스 수에 관계없이 오직 하나의 복사본만 존재하는데, 이를 type properties라고 부릅니다.

타입 속성은 특정 타입의 모든 인스턴스에 보편적인 값을 정의할 때 유용합니다. 예를 들어 C언어의 static 상수처럼 모든 인스턴스가 사용할 수 있는 상수 속성이나, C언어의 static 변수처럼 해당 타입의 모든 인스턴스에 전역인 값을 저장하는 변수 속성 등이 있겠네요.

저장 타입 속성은 변수 또는 상수일 수 있습니다. 반면 계산 타입 속성은 계산 인스턴스 속성과 마찬가지로 항상 변수 속성으로 선언됩니다.

타입 속성 문법

C와 Objective-C에서는 타입과 관련된 static 상수와 변수를 전역 static 변수로 정의합니다. 그러나 Swift에서 타입 속성은 타입 정의의 일부로 타입의 외부 중괄호 내에 작성되며, 각 타입 속성은 명시적으로 해당 타입으로 범위가 지정됩니다.

타입 속성은 static 키워드로 정의합니다. 클래스 타입의 계산 타입 속성의 경우 하위 클래스가 상위 클래스의 구현을 재정의할 수 있도록 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
    }
}
swift

타입 속성 조회 및 설정

타입 속성은 인스턴스 속성과 마찬가지로 점 구문을 사용하여 쿼리하고 설정합니다. 그러나 타입 속성은 해당 타입의 인스턴스가 아닌 타입 자체에서 쿼리하고 설정합니다. 예를 들면:

print(SomeStructure.storedTypeProperty)
// "Some value." 출력
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// "Another value." 출력
print(SomeEnumeration.computedTypeProperty)
// "6" 출력
print(SomeClass.computedTypeProperty)
// "27" 출력
swift

다음 예제에서는 여러 오디오 채널의 오디오 레벨 미터를 모델링하는 구조체의 일부로 두 개의 저장 타입 속성을 사용합니다. 각 채널은 0에서 10까지의 정수 오디오 레벨을 가집니다.

아래 그림은 이러한 오디오 채널 두 개를 결합하여 스테레오 오디오 레벨 미터를 모델링하는 방법을 보여줍니다. 채널의 오디오 레벨이 0이면 해당 채널의 조명이 켜지지 않습니다. 오디오 레벨이 10이면 해당 채널의 모든 조명이 켜지겠죠. 이 그림에서 왼쪽 채널의 현재 레벨은 9이고 오른쪽 채널의 현재 레벨은 7입니다:

lecture image

위에서 설명한 오디오 채널은 AudioChannel 구조체의 인스턴스로 표현됩니다:

struct AudioChannel {
    static let thresholdLevel = 10
    // 모든 채널의 최대 임계값 레벨은 10으로 설정
 
    static var maxInputLevelForAllChannels = 0
    // 모든 채널의 최대 입력 레벨을 추적하는 변수. 초기값은 0
 
    var currentLevel: Int = 0 {
        didSet {
            if currentLevel > AudioChannel.thresholdLevel {
                // 새 오디오 레벨이 임계값 레벨보다 높으면
                // 임계값 레벨로 제한
                currentLevel = AudioChannel.thresholdLevel
            }
            if currentLevel > AudioChannel.maxInputLevelForAllChannels {
                // 현재 레벨이 이전에 수신된 모든 AudioChannel 인스턴스의
                // 최대 입력 레벨보다 높으면 새 전체 최대 입력 레벨로 저장
                AudioChannel.maxInputLevelForAllChannels = currentLevel
            }
        }
    }
}
swift

AudioChannel 구조체는 기능을 지원하기 위해 두 개의 저장 타입 속성을 정의합니다. 첫 번째인 thresholdLevel은 오디오 레벨이 가질 수 있는 최대 임계값을 정의하는데, 모든 AudioChannel 인스턴스에 대해 10으로 고정된 상수 값입니다. 오디오 신호가 10보다 큰 값으로 들어오면 이 임계값으로 제한됩니다(아래 설명 참고).

두 번째 타입 속성은 maxInputLevelForAllChannels이라는 변수 저장 속성입니다. 이는 모든 AudioChannel 인스턴스에서 수신한 최대 입력 값을 추적하며, 초기값은 0입니다.

AudioChannel 구조체는 또한 0에서 10 범위의 채널 현재 오디오 레벨을 나타내는 currentLevel이라는 저장 인스턴스 속성을 정의합니다.

currentLevel 속성에는 currentLevel이 설정될 때마다 값을 확인하는 didSet 속성 옵저버가 있습니다. 이 옵저버는 두 가지 검사를 수행하는데요:

  • currentLevel의 새 값이 허용된 thresholdLevel보다 큰 경우 속성 옵저버는 currentLevelthresholdLevel로 제한합니다.
  • currentLevel의 새 값(제한 후)이 이전에 모든 AudioChannel 인스턴스에서 수신한 값보다 높은 경우 속성 옵저버는 새 currentLevel 값을 maxInputLevelForAllChannels 타입 속성에 저장합니다.

스테레오 사운드 시스템의 오디오 레벨을 나타내기 위해 leftChannelrightChannel이라는 두 개의 새 오디오 채널을 만들 때 AudioChannel 구조체를 사용할 수 있습니다:

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

left 채널의 currentLevel7로 설정하면 maxInputLevelForAllChannels 타입 속성이 7과 같도록 업데이트되는 것을 볼 수 있습니다:

leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
// "7" 출력
print(AudioChannel.maxInputLevelForAllChannels)
// "7" 출력
swift

right 채널의 currentLevel11로 설정하려고 하면 오른쪽 채널의 currentLevel 속성이 최대값 10으로 제한되고, maxInputLevelForAllChannels 타입 속성이 10과 같도록 업데이트되는 것을 확인할 수 있겠죠:

rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
// "10" 출력
print(AudioChannel.maxInputLevelForAllChannels)
// "10" 출력
swift

이처럼 타입 속성을 활용하면 특정 타입의 모든 인스턴스에 적용되는 상수나 변수를 편리하게 정의할 수 있답니다. 인스턴스 속성과 달리 타입 자체에 속하기 때문에 해당 타입의 모든 인스턴스에서 공유되는 값을 저장하는 데 유용하게 사용될 수 있겠네요.

var leftChannel = AudioChannel()
var rightChannel = AudioChannel()
 
leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
// "7" 출력
print(AudioChannel.maxInputLevelForAllChannels)
// "7" 출력
 
rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
// "10" 출력
print(AudioChannel.maxInputLevelForAllChannels)
// "10" 출력
swift

위의 코드를 터미널에서 실행해보면 타입 속성의 동작을 직접 확인해볼 수 있습니다. 오디오 채널의 레벨을 설정하고 쿼리해보면서 maxInputLevelForAllChannels 타입 속성이 어떻게 업데이트되는지 살펴보세요.