[Programming Language]/[Swift]

[Swift] 프로퍼티(Property) [Stored •Computed, getter, setter, read-only]

Semincolon 2024. 11. 5. 09:21

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)가 값 타입이기 때문인데 이에 대한 내용은 아래 포스팅에서 추가적으로 확인할 수 있다.

https://semin1127.tistory.com/entry/Swift-%EA%B5%AC%EC%A1%B0%EC%B2%B4Structure-vs-%ED%81%B4%EB%9E%98%EC%8A%A4Class-%EC%B0%A8%EC%9D%B4%EC%A0%90

 

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))

 

현재 상태에서 rect1center을 출력해보면 중점 좌표인 (5, 5)가 계산되어 출력된다. 이는 centerget 부분이 실행된 것이다.

print(rect1.center) // Point(x: 5.0, y: 5.0)

 

중점 좌표를 (10, 10)으로 변경해보았다. 그럼 centerset 부분이 실행되어 원점의 좌표기존 (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)는 자주 사용되므로 형태를 익혀두면 도움이 될 것 같다..!

 

끝!