Swift) Property

6 minute read

Property

프로퍼티는 값을 특정 클래스.구조체 또는 열겨형과 연결한다.

저장된 프로퍼티는 인스턴스의 일부로 상수 및 변수 값을 저장하는 반면

연산 프로퍼티는 값을 저장하는 대신 계산한다.

연산 프로퍼티는 클래스. 구조체 및 열거형에 의해 제공된다.

저장 프로퍼티는 클래스와 구조체에 의해서만 제공된다.

저장 및 연산 된 프로퍼티는 일반적으로 특정 타입의 인스턴스와 연결된다.

다만 프로퍼티는 타입 자체와도 연관 될 수 있다. 이러한 속석을 타입프로퍼티라고 한다.


프로퍼티 옵저버를 정의하여 프로퍼티 값의 변경 사항을 모니터링 할 수 있고.

커스텀 액션으로 응답할 수 있다.

프로퍼티 옵저버는 사용자가 직접 정의한 저장 프로퍼티와 하위 클래스가

수퍼 클래스에서 상속하는 프로퍼티에 추가할 수 있다.

Property Wrapper를 사용하여 여러 프로퍼티의 getter 및 setter에서

코드를 재사용 할 수도 있다.


저장 프로퍼티

가장 간단한 형태로 저장된 프로퍼티는 특정 클래스 또는 구조체의

인스턴스의 일부로 저장되는 상수 또는 변수이다.

변수를 통한 가변 프로퍼티와 상수를 통해 저장한 상수 저장 프로퍼티가 있다.

struct Henry {
    var age: Int
    let gender: String
}
var myName = Henry(age: 15, gender: "male")
myName.age = 20
print("Henry의 성별은 \(myName.gender)이고, 5년 후 나이는 \(myName.age)살 이다.")

헨리라는 타입에서 헨리의 성별은 변하지 않지만 나이는 매년 바뀌므로 변수로 설정.


상수 구조체 인스턴스의 저장 프로퍼티

구조체의 인스턴스를 상수에 할당하면 변수 프로퍼티로 선언 된 경우에도

인스턴스의 속성을 수정할 수 없다.

let myName = Henry(age:20, gender: "male")
myName.age = 29 // Error 변수 프로퍼티에 접근하지만 상수 인스턴스로 선언되어 갑 변경 안됨.

그 이유는 구조체가 값 타입이기 때문이다.

값 타입의 인스턴스가 상수로 표시되면 모든 프로퍼티도 마찬가지 이다.

참조 타입인 클래스의 경우에는 인스턴스를 상수에 할당해도

해당 인스턴스의 변수 속성을 변경할 수 있다.


지연 저장 프로퍼티

지연 저장 프로퍼티는 처음 사용될 때까지 초기 값이 계산되지 않는 프로퍼티이다.

lazy 키워드를 선언 전에 작성하여 지연 저장 프로퍼티를 나타낸다.

인스턴스 초기화가 완료 될 때까지 초가 값이 검색되지 않을 수 있으므로
항상 lazy프로퍼티를 변수로 선언해야한다.
상수 프로퍼티는 초기화가 완료되기 전에 항상 값을 가져야하므로
lazy로 선언 할 수 없습니다.

지연 프로퍼티는 프로퍼티의 초기 값이 인스턴스 초기화가 완료 될 때까지

값을 알 수 없는 외부 요인에 따라 달라지는 경우에 유용하다.

프로퍼티의 초기 값에 복잡하거나 연산이 많이 드는 설정이 필요할 때

또는 필요할 때까지 수행해서는 안되는 경우에도 유용하다.

class DataImporter {
    /* 이 클래스는 외부 파일에서 데이터를 가져오는 클래스이다.
        이 클래스는 초기화하는 데 적은 시간이 걸린다고 가정한다.
    */
    var filename = "data.txt"
    // 이 곳에서 데이터 가져 오기 기능을 제공
}

class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
    // 이 곳에서 데이터 관리 기능을 제공
}

let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// importer 프로퍼티에 대한 DataImporter 인스턴스가 아직 미생성.

DataManager 클래스에는 새로운 빈 문자열 값이 배열로 초기화되는

data라는 저장 프로퍼티가 있다. 나머지 기능은 표시되지 않지만

DataManager 클래스의 목적은 이 문자열 데이터 배열을 관리하고 접근을 제공하는 것이다.

DataImporter 클래스에 의해 제공된 기능 중 DataManager 클래스는 파일에서

데이터를 가져오는 기능이이 있다. 초기화 하는데 적은 시간이 걸린다고 가정되어있다.

이는 DataImporter 인스턴스가 초기화 될 때 그 인스턴스가 파일을 열고 해당 내용을

메모리로 읽어야 하기 때문일 수 있다.

DataManager 인스턴스가 파일에서 데이터를 가져오지 않고도 데이터를 관리 할 수 있기 때문에

DataManager 자체가 생성 될 때 새 DataImporter 인스턴스를 생성하지 않는다.

대신 DataImporter 인스턴스를 처음 사용할 때 생성하는 것이 더 합리적이다.

laze로 선언된 변수 DataImporter 인스턴스는 importer 프로퍼티에 처음 접근할 때만 생성된다.

print(manager.importer.filename) // "data.txt"
// importer 프로퍼티에 대한 인스턴스가 이제 생성되었다.
지연 저장 프로퍼티는 여러 스레드에서 동시에 접근되고,
프로퍼티가 아직 초기화되지 않은 경우 프로퍼티가 한 번만 초기화 된다는 보장은 없다.

인스턴스 변수의 저장 프로퍼티

스위프트 프로퍼티에는 해당 인스턴스 변수가 없으며 프로퍼티의 백업 저장소에

직접 접근 할 수 없다. 이 접근 방식은 서로 다른 컨텍스트에서 값에 접근하는 방법에 대한

혼동을 방지하고 프로퍼티 선언을 하나의 명확한 문으로 단순화한다.

이름, 타입 및 메모리 관리 특성을 포함하여 프로퍼티에 대한 모든 정보는

타입 정의의 일부로 단우리 위치에 정의된다.


연산 프로퍼티

저장 프로퍼티 외에도 클래스, 구조체 및 열거형은 실제로 값을 저장하지 않는

연산 프로퍼티를 정의 할 수 있다.

대신 다른 프로퍼티와 값을 간접적으로 검색하고 설정하기 위해 getter와 옵셔널 setter를 제공한다.

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)
        }
    }
}
var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// "square.origin is now at (10.0, 10.0)"

위 예제의 타입 설명

  • Point - x, y 좌표를 캡슐화
  • Size - width와 height를 캡슐화
  • Rect - Point와 Size로 사각형을 정의

Rect 구조체는 center라는 연산 프로퍼티를 제공한다.

현재 중심 위치는 항상 Point와 Size로 결정할 수 있으므로 센터 포인트를 명시적인

Point 프로퍼티로 저장할 필요가 없다.

대신 Rect는 center라는 연산 프로퍼티 변수에 대해 사용자 정의 getter 및 setter를

정의하여 마치 실제 저장 프로퍼티 인 것처럼 사각형의 중심으로 작업 할 수 있도록 한다.

square라는 새로운 변수로 Rect인스턴스를 생성 후 Point(0, 0), 너비와 높이 10으로 초기화되었다.

그런 다음 square.center를 통해 center프로퍼티에 접근하여

center에 대한 getter를 호출하여 현재 프로퍼티 값을 검색한다.

기존 값을 반환하는 대신 getter는 실제로 사각형의 중심을 나타내는 새 Point를 계산하고 반환한다.

getter는 (5, 5)의 Center Point를 올바르게 반환한다.

그 후 center 프로퍼티가 (15, 15)의 새 값으로 설정되어 사격형 아래 다이어그램에서

주황색 사각형으로 표시된 새 위치로 위쪽 및 오른쪽으로 이동한다.

center 프로퍼티를 설정하면 center에 대한 setter가 호출되어 저장된 origin 프로퍼티의

x,y 값을 수정하고, 사각형을 새 위치로 이동시킨다.

스크린샷 2021-03-12 오후 9 04 37


Shorthand Setter 선언

연산 프로퍼티 setter가 설정할 새 값의 이름을 정의하지 않으면 기본 이름 인 newValue가 사용된다.

struct AlternativeRect {
    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 {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}
// setter에 (이름)을 정의하지 않아 기본값으로 newValue로 사용

Shorthand Getter 선언

getter의 전체 본문이 단일 표현식 인 경우 getter는 해당 표현식을 암시적으로 반환한다.

다음 예제는 속기 표기법과 setter의 속기 표기법을 활용하는 Rect 구조체의 다른 사용법이다.

struct CompactRect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            Point(x: origin.x + (size.width / 2),
                  y: origin.y + (size.height / 2))
        }
        set {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}

getter에서 반환을 생략하는 것은 함수에서 반환을 생략하는 것과 같은 규칙을 따른다.


읽기 전용 연산 프로퍼티

getter가 있지만 setter가 없는 계산 된 속성을 읽기 전용 연산 프로퍼티라고 부른다.

읽기 전용 연산 프로퍼티는 항상 값을 반환하며 도트를 통해 접근할 수 있지만

다른 값으로 설정할 수 없다.

읽기 전용 연산 프로퍼티를 포함한 연산 프로퍼티는 값이 고정되어 있지 않으므로
변수 프로퍼티로 선언해야 한다. 상수는 인스턴스 초기화의 일부로 설정되면
값을 변경할 수 없음을 나타내기 위해 상수 프로퍼티에만 사용된다.

get 키워드와 해당 중괄호를 제거하여 일기전용 프로퍼티의 선언을 단순화 할 수 있다.

struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    var volume: Double {
        return width * height * depth
    }
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// "the volume of fourByFiveByTwo is 40.0"

이 예제는 width, height, depth 프로퍼티에 있는 3D 직사각형 상자를 나타내는

Cuboid라는 새 구조체 인스턴스를 정의한다.

이 인스턴스에는 volume이라는 읽기 전용 연산 프로퍼티도 있다.

특정 볼륨 값에 대해 너비, 높이, 및 깊이 값을 사용해야하는 것이 모호하기 때문에 볼륨을

설정할 수 있는 것은 의미가 없다. 그럼에도 불구하고 Cuboid는 읽기 전용 연산 프로퍼티를 제공하여

외부 사용자가 현재 계산 된 볼륨을 검색 할 수 있도록하는 것이 유용하다.

Comments