1. 저장된 프로퍼티(Stored Property) vs 계산된 프로퍼티(Computed Property)
프로퍼티는 저장하는 값에 따라 저장된 프로퍼티와 계산된 프로퍼티로 나뉜다.
1-1. 저장된 프로퍼티(Stored Property)
먼저 저장된 프로퍼티란 우리가 다른 언어에서도 흔히 사용했던 멤버 변수의 개념으로, 다른 변수에 관계없이 스스로의 값을 갖는 변수 또는 상수를 말한다.
struct Person {
var name: String
let age: Int
}
var p1 = Person(name: "John", age: 20)
Person 구조체는 저장된 프로퍼티 변수(Variable Stored Property)인 name과 저장된 프로퍼티 상수(Constant Stored Property)인 age를 가진다. 구조체의 인스턴스 p1을 생성할 땐 두 개의 프로퍼티에 초깃값을 할당해야 한다.
1-1-1. 저장된 프로퍼티의 값 변경
Person 구조체에서 var 키워드로 선언된 name의 값은 변경이 가능하나, let 키워드로 선언된 age의 값은 변경할 수 없다.
여기서 Person의 인스턴스인 p1은 var 키워드를 통해 선언되었다. 이는 값이 변할 수 있는 변수이므로 name 프로퍼티의 값을 변경할 수 있었던 것이다. 그렇다면 만약 인스턴스가 let 키워드를 통해 선언된 상수일 경우에는 name 프로퍼티의 값을 변경할 수 있을까?
p1의 선언을 let 키워드로 변경하자 name 프로퍼티의 값을 변경할 수 없다는 오류가 발생하게 된다. 이는 구조체(struct)가 값 타입이기 때문인데 이에 대한 내용은 아래 포스팅에서 추가적으로 확인할 수 있다.
1-2. 계산된 프로퍼티(Computed Property)
계산된 프로퍼티란 실제 값을 가지는 것이 아니라 다른 변수나 상수의 값에 대한 연산 결과를 조회하거나 다른 변수의 값을 설정할 때 사용되는 변수를 말한다. 이때 계산된 프로퍼티는 항상 같은 값을 반환하는 것이 아니기에 반드시 var 키워드로만 선언할 수 있으며 let 키워드를 사용하면 오류가 발생한다. 아래 코드는 공식 문서에서 사용된 코드이다.
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) {
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
여기서 구조체 Rect는 원점을 뜻하는 origin과 크기를 뜻하는 size를 갖는다. 추가로 계산된 프로퍼티인 center를 갖는데 Point 타입으로 선언된 것을 확인할 수 있다. 이는 값을 조회할 때는 Point 타입의 값을 반환한다는 것이고, 값을 설정할 때는 Point 타입의 값을 인수로 받아 설정한다는 것을 뜻한다.
1-2-1. 값을 조회할 때 사용하는 get, 값을 설정할 때 사용하는 set
계산된 프로퍼티는 실질적으로 값을 저장하는 것이 아니기에 게더(Getter)와 세더(Setter)를 사용하여 값을 조회하거나 설정할 수 있다. 이 두 개념은 다른 언어에서도 사용되는 개념이다.
위 코드에서 계산된 프로퍼티 center는 원점(origin)과 크기(size) 값을 계산하여 현재 사각형(Rect)의 중점 좌표를 반환한다. 이때 center의 타입이 Point이므로 반환 타입 역시 Point인 것을 확인할 수 있다.
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
또한 세더에서는 새로운 값을 받아 이를 기준으로 원점의 좌표를 다시 설정하고 있다. 여기서 사용된 newCenter은 전달된 새로운 값을 나타내는 라벨이라고 볼 수 있는데 만약 별도의 이름을 붙이지 않는다면 기본적으로 newValue라는 값으로 사용할 수 있다.
set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
1-2-2. get, set의 사용 예시
Rect 구조체의 인스턴스를 생성해보았다.
// 원점이 (0, 0)이고 한 변의 길이가 10인 정사각형
var rect1 = Rect(origin: Point(x: 0, y: 0), size: Size(width: 10, height: 10))
현재 상태에서 rect1의 center을 출력해보면 중점 좌표인 (5, 5)가 계산되어 출력된다. 이는 center의 get 부분이 실행된 것이다.
print(rect1.center) // Point(x: 5.0, y: 5.0)
중점 좌표를 (10, 10)으로 변경해보았다. 그럼 center의 set 부분이 실행되어 원점의 좌표가 기존 (0, 0)에서 (5, 5)로 변경된다.
rect1.center = Point(x: 10.0, y: 10.0)
print(rect1.origin) // Point(x: 5.0, y: 5.0)
print(rect1.center) // Point(x: 10.0, y: 10.0)
1-2-3. 읽기 전용 계산된 프로퍼티(Read-Only Computed Property)
만약 계산된 프로퍼티를 통해 값을 설정할 필요가 없다면 set을 사용할 필요가 없을 것이다. 이처럼 get만 존재하는 계산된 프로퍼티는 읽기 전용 계산된 프로퍼티(Read-Only Computed Property)라고 말한다.
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
}
읽기 전용 프로퍼티는 어차피 get 밖에 없으므로 다음과 같이 get 키워드를 생략할 수 있다.
var center: Point {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
만약 하나의 문장만 가진다면 return 키워드도 생략할 수 있다.
var center: Point {
Point(x: origin.x + (size.width / 2), y: origin.y + (size.height / 2))
}
2. 타입 프로퍼티(Type Property)
지금까지 본 프로퍼티는 생성한 인스턴스별로 고유한 값을 가지는 인스턴스 프로퍼티였다면, 타입 프로퍼티는 타입별로 고유한 값을 가지는 프로퍼티다. 저장된 프로퍼티와 계산된 프로퍼티 모두 타입 프로퍼티로 선언할 수 있다.
타입 프로퍼티의 선언은 static 키워드를 사용하여 할 수 있고, 클래스의 경우 class 키워드를 대신 사용하여 하위 클래스에서 상위 클래스를 재정의할 수 있도록 할 수도 있다. 저장된 타입 프로퍼티는 반드시 초깃값을 정의해야 한다.
struct MyStruct {
static var year = 2024
static var month: Int {
return 11
}
}
// 타입 자체로 접근 가능
print(MyStruct.year) // 2024
print(MyStruct.month) // 11
// 인스턴스로는 접근 불가능
let myStruct = MyStruct()
print(myStruct.year) // Static member 'year' cannot be used on instance of type 'MyStruct'
계산된 프로퍼티(Computed Properties)는 자주 사용되므로 형태를 익혀두면 도움이 될 것 같다..!
끝!
'[Programming Language] > [Swift]' 카테고리의 다른 글
[Swift] 메서드(Methods) [인스턴스•타입 메서드, mutating, self] (1) | 2024.11.07 |
---|---|
[Swift] 프로퍼티 관찰자(Property Observers) (0) | 2024.11.06 |
[Swift] 구조체(Structure) vs 클래스(Class) 차이점 (4) | 2024.11.04 |
[Swift] ~= 연산자 : 범위 확인 연산자, 패턴 매치 연산자 (0) | 2024.10.24 |
[Swift] String.allSatisfy(_:) 함수란? (0) | 2024.09.23 |