본문 바로가기
iOS/Swift 문법 복습

프로퍼티 (Properties)

by 황민우 2022. 6. 27.

프로퍼티 정의

- 프로퍼티는 구조체, 클래스, 열거형 내부에 구현할 수 있습니다.
- 다만 열거형 내부에는 연산 프로퍼티만 구현할 수 있습니다.
- 연산 프로퍼티는 var로만 선언할 수 있습니다. 
- 연산 프로퍼티를 읽기 전용으로는 구현할 수 있지만, 쓰기 전용으로는 구현할 수 없습니다
- 읽기 전용으로 구현하려면 get 블록만 작성해주면 됩니다. 
- 읽기전용은 get블록을 생략할 수 있습니다. 
- 읽기, 쓰기 모두 가능하게 하려면 get 블록과 set블록을 모두 구현해주면 됩니다.
- set 블럭에서 암시적 매개변수 newValue를 사용할 수 있습니다.

 

프로퍼티 종류

1, 저장 프로퍼티(Stored Properties)
2, 지연 저장 프로퍼티(Lazy Stroed Properties)
3, 연산 프로퍼티(Computed Properties)
4, 프로퍼티 감시자(Property Observers)
5, 타입 프로퍼티(Type Properties)

 


1, 저장 프로퍼티(Stored Properties)

- 가장 단순한 개념의 프로퍼티로써 클래스 또는 구조체의 인스턴스와 연관된 값을 저장하는 프로퍼티입니다.

 

구조체의 저장 프로퍼티가 옵셔널이 아니더라도, 구조체는 저장 프로퍼티를 모두 포함하는 이니셜라이저를 자동으로 생성합니다.
하지만 클래스의 저장 프로퍼티는 옵셔널이 아니라면 프로퍼티 기본값을 지정해주거나 사용자정의 이니셜라이저를 통해 반드시 초기화해주어야 합니다.

struct FixedLengthRange {
    var firstValue: Int		// 저장 프로퍼티
    let length: Int		// 저장 프로퍼티
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// 범위 값은 0, 1, 2 입니다.
rangeOfThreeItems.firstValue = 6
// 범위 값은 6, 7, 8 입니다.

 

 

 

2, 지연 저장 프로퍼티(Lazy Stroed Properties)

- 지연 저장 프로퍼티는 값이 처음으로 사용 되기 전에는 계산되지 않는 프로퍼티입니다. 
- 지연 저장 프로퍼티로 선언하기 위해서는 프로퍼티의 선언 앞에 lazy 키워드를 붙이면 됩니다.



지연 프로퍼티는 반드시 변수(var)로 선언해야 합니다. 왜냐하면 상수는 초기화가 되기 전에 항상 값을 같은 프로퍼티인데, 지연 프로퍼티는 처음 사용되기 전에는 값을 갖지 않는 프로퍼티이기 때문입니다.

 



- 지연 프로퍼티는 프로퍼티가 특정 요소에 의존적이어서 그 요소가 끝나기 전에 적절한 값을 알지 못하는 경우에 유용합니다. 
- 또 복잡한 계산이나 부하가 많이 걸리는 작업을 지연 프로퍼티로 선언해 사용하면 실제 사용되기 전에는 실행되지 않아서 인스턴스의 초기화 시점에 복잡한 계산을 피할 수 있습니다.

class DataImporter {
    /*
        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")
// DataImporter 인스턴스는 이 시점에 생성돼 있지 않습니다.

- manager.importer.filename가 실행돼 실제 importer 프로퍼티에 처음 접근할 때 비로소 importer인스턴스는 생성됩니다.

print(manager.importer.filename)
// the DataImporter 인스턴스가 생성되었습니다.
// "data.txt" 파일을 출력합니다.


3, 연산 프로퍼티(Computed Properties)

- 저장 프로퍼티를 뿐 아니라 추가적으로 클래스, 구조체, 열거형은 계산된 프로퍼티를 선언할 수 있습니다.

- 이 계산된 프로퍼티는 실제 값을 저장하고 있는 것이 아니라 getter와 optional한 setter를 제공해 값을 탐색하고 간접적으로 다른 프로퍼티 값을 설정할 수 있는 방법을 제공합니다.

 

연산 프로퍼티는 반드시 let이 아니라 var로 선언해야 합니다.

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)" 출력

 

- 위 코드는 좌표와 크기를 갖는 사각형을 표현하는 구조체에 관한 코드입니다.

- 여기서 Rect구조체는 사각형의 중점을 표현하는 center라는 계산된 프로퍼티를 제공합니다.

- 이 프로퍼티는 계산된 프로퍼티의 정의대로 값을 직접 갖고 있는 것이 아니라 다른 좌표와 크기 프로퍼티들을 적절히 연산해서 구할 수 있습니다.(get).

- 또 setset으로 사각형의 중점을 직접 설정할 수 있는데, 이 값을 설정할 때 x, y좌표가 어떤 값을 가져야 하는지 계산해서 x, y에 적절한 좌표값을 넣어 줍니다

 

Setter 선언의 간략한 표현

- 앞의 코드에서는 Setter의 인자 이름을 아래와 같이 set(newCenter)라고 명시했지만, 만약 이렇게 (newCenter)라고 인자 이름을 지정하지 않으면 인자 기본 이름인 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)
        }
    }
}

- 위 코드에서는 set메서드 안에서 인자 이름을 지정하지 않았는데도 newValue.x, newValue.y를 사용할 수 있는 것을 보실 수 있습니다.

 

 

읽기 전용 연산 프로퍼티

- getter만 있고 setter를 제공하지 않는 계산된 프로퍼티를 읽기전용 계산된 프로퍼티라고 합니다.

- 즉, 읽기전용 계산된 프로퍼티는 반드시 반환 값을 제공하고 다른 값을 지정할 수는 없는 프로퍼티입니다.

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" 출력

 


4, 프로퍼티 감시자(Property Observers)

- 프로퍼티에는 새 값이 설정(set) 될 때마다 이 이벤트를 감지할 수 있는 옵저버를 제공합니다.

- 이 옵저버를 프로퍼티 옵저버라 하는데 프로퍼티 옵저버는 새 값이 이전 값과 같더라도 항상 호출됩니다.

- 이 프로퍼티 옵저버는 지연 저장 프로퍼티(lazy stored properties에서는 사용할 수 없습니다.

- 서브클래스의 프로퍼티에 옵저버를 정의하는 것도 가능합니다.

- 계산된 프로퍼티는 setter에서 값의 변화를 감지할 수 있기 때문에 따로 옵저버를 정의할 필요가 없습니다.

- 프로퍼티에서는 다음 두 가지 옵저버를 제공합니다.

 

     willSet : 값이 저장되기 바로 직전에 호출됨

     didSet :  새 값이 저장되고 난 직후에 호출됨

 

willSet에서는 새 값의 파라미터명을 지정할 수 있는데, 지정하지 않으면 기본값으로 newValue를 사용합니다.

didSet에서는 바뀌기 전의 값의 파라미터명을 지정할 수 있는데, 지정하지 않으면 기본 값으로 oldValue를 사용합니다.

class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("About to set totalSteps to \(newTotalSteps)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("Added \(totalSteps - oldValue) steps")
            }
        }
    }
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps

 

5, 타입 프로퍼티(Type Properties)

 

- 인스턴스 프로퍼티는 특정 인스턴스에 속한 프로퍼티를 말합니다.

- 이 프로퍼티는 새로운 인스턴스가 생성될 때마다 새로운 프로퍼티도 같이 생성됩니다.

- 타입 프로퍼티는 특정 타입에 속한 프로퍼티로 그 타입에 해당하는 단 하나의 프로퍼티만 생성됩니다.

- 이 타입 프로퍼티는 특정 타입의 모든 인스턴스에 공통으로 사용되는 값을 정의할 때 유용합니다.

 

인스턴스 프로퍼티와는 다르게 타입 프로퍼티는 항상 초기값을 지정해서 사용해야 합니다.

왜냐하면 타입 자체에는 초기자(Initializer)가 없어 초기화할 곳이 없기 때문입니다.

 

 

- 타입 프로퍼티를 선언을 위해서는 static 키워드를 사용합니다.

- 클래스에서는 staticclass 이렇게 2가지 형태로 타입 프로퍼티를 선언할 수 있는데 두 가지 경우의 차이는 서브클래스에서overriding가능 여부입니다.

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

 

접근과 설정

- 인스턴스 프로퍼티와 마찬가지로 타입 프로퍼티도 점 연산자(dot operator)로 프로퍼티의 값을 가져오고 할당할 수 있습니다.

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"

내용 출처, 참고 자료

https://jusung.gitbook.io/the-swift-language-guide/language-guide/10-properties#type-properties

 

프로퍼티 (Properties) - The Swift Language Guide (한국어)

프로퍼티는 클래스, 구조체, 열거형과 관련한 값입니다. 프로퍼티의 종류에는 저장 프로퍼티(Stored Properties)와 계산된 프로퍼티(Computed Properties)가 있습니다. 저장 프로퍼티는 말 그대로 값을 저

jusung.gitbook.io

https://jinshine.github.io/2018/05/22/Swift/6.%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0(Property)/ 

 

[Swift] 프로퍼티(Property) - jinShine

프로퍼티(Property) 1. 프로퍼티 프로퍼티는 크게 5가지가 존재합니다. 저장 프로퍼티(Stored Properties) 지연 저장 프로퍼티(Lazy Stroed Properties) 연산 프로퍼티(Computed Properties) 프로퍼티 감시자(Property Obs

jinshine.github.io

 

'iOS > Swift 문법 복습' 카테고리의 다른 글

(인스턴스, 스태틱, 클래스) 메서드  (0) 2022.06.30
프로토콜 [Protocol]  (0) 2022.06.29
옵셔널 [Optional]  (0) 2022.06.28
Struct와 Class, Enum의 차이를 설명하시오.  (0) 2022.06.25

댓글