프로퍼티
- 클래스, 구조체, 열거형과 관련한 값
- 프로퍼티 옵저버를 정의하여 값이 변할때마다 모니터링 가능
- 저장 프로퍼티 : 값을 저장하고 있음
- 사용 : 클래스, 구조체
- 계산된 프로퍼티 : 값을 저장하지 않고, 특정하게 계산한 값을 반환
- 사용 : 클래스, 구조체, 열거형
저장 프로퍼티 (Stored Properties)
- 단순히 값을 저장하고 있는 프로퍼티
- let, var 키워드 이용해서 변수 선언해 사용
struct FixedLengthRange {
var firstValue: Int
let length: Int
}
// ---> 변수 구조체
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// 범위 값은 0, 1, 2 입니다.
rangeOfThreeItems.firstValue = 6
// 범위 값은 6, 7, 8 입니다.
// ---> 상수 구조체
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// 범위 값은 0, 1, 2, 3 입니다.
rangeOfFourItems.firstValue = 6
// 에러 발생! 인스턴스의 프로퍼티 값 변경 안됨
지연 저장 프로퍼티 (Lazy Stored Properties)
- 값이 처음 사용되기 전에는 계산되지 않음
- lazy property는 반드시 var 로 선언되야 함 !
- 왜냐하면 상수는 초기화 되기전 항상 값을 갖는 프로퍼티인데, lazy 프로퍼티는 처음 사용되기 전엔 값을 가지지 않으므로
- (없다 생겨서 변하므로 상수로 선언해야 된다는 뜻인듯)
- 특정 요소에 의존적이여서 그 요소가 끝나기 전에 적절한 값을 알지 못하는 경우에 유용
- 복잡한 계산 / 부하가 많이 걸리는 작업 에서 선언해 사용하면 실제 사용 전에 실행되지 않아 인스턴스 초기화 시점에 복잡한 계산 피할 수 있음
예시
- DataManager는 데이터를 가져오는 DataImporter를 가지고 있음
- DataImporter 는 실제 디스크 파일에서 데이터를 갖고와서 초기화 때 많은 시간 필요 → 🟢 그래서 importer를 lazy로 선언
- 🔵DataManager 인스턴스를 생성하고 거기에 data 넣어도 그 시점에 DataImporter 인스턴스는 생성되지 않음.
- 🟣 importer에 접근하는 순간 그제서야 인스턴스가 생성된다
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")
🟣 print(manager.importer.filename)
만약, lazy property가 여러 스레드에서 사용되면 한번만 실행되는 걸 보장하지 않습니다. 단일 스레드에서 사용되면 초기화는 한번만 한다.
계산된 프로퍼티 (Computed Properties)
- 실제 값을 저장하고 있는게 아니라 getter 와 옵셔널한 setter 를 제
- 값을 탐색하고 간접적으로 다른 프로퍼티 값을 설정할 수 있는 방법 제공
- 반드시 var 로 선언되야 함
예시
- 좌표와 크기를 갖는 사각형을 표현하는 구조체
- center라는 계산된 프로퍼티를 제공, origin과 size를 이용해서 값을 직접 갖고있는 것이 아닌 적절히 연산해서 구함
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)" 출력
- setter에서 인자이름을 명시하지 않으면 기본 인자이름인 newValue 사용
set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
읽기전용 계산된 프로퍼티 (Read-Only Computed Properties)
- getter만 있고, setter 없음
- 반환 값을 제공하고, 다른 값을 지정할 수는 없는 프로퍼티
- 반드시 var 로 선언되야 함.
- 보통 read-only는 한번 정해지면 변하지 않아 let 으로 선언하는 것이 맞으나, 계산된 프로퍼티는 read-only라도 계산 값에 따라 변경되기 때문에 var로 선언되야한다.
예시
- volume 이라는 읽기전용 계산된 프로퍼티 사용
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" 출력
프로퍼티 옵져버 (Property Observers)
- 프로퍼티에 새 값이 set 될 때마다 이벤트를 감지할 수 있는 옵져버 제공
- 새 값이 이전과 같아도 항상 호출됨
- 지연 저장 프로퍼티(lazy) 에서는 사용할 수 없음
- 서브 클래스의 프로퍼티에 옵저버 정의하는 것 가능
- computed property는 setter에서 이미 값의 변화를 감지하기 때문에 옵져버 정의할 필요 없음
종류
- 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
만약, in-out 파라미터로 선언된 함수의 인자에 프로퍼티 넘기면 willSet , didSet 항상 실행된다. in-out 파라미터는 프로퍼티가 항상 copy 되기 때문에, 원래 값에 새 값을 다시 덮어쓰게 된다.
전역변수와 지역변수
- computed property, 옵저버 기능 모두 전역변수와 지역변수에 사용 가능
- 전역 상수와 변수는 지연 저장 프로퍼티와 같이 지연 계산(lazy computed)된다. 하지만 lazy 키워드 붙일 필요 없음 (전역으로 사용되는 상수, 변수는 값이 사용될 때 변경되어서 인가..?)
- 반면 지역상수와 변수는 지연 계산될 수 없음
타입 프로퍼티(Type Properties)
- 특정 인스턴스에 속한 프로퍼티
- 새로운 인스턴스가 생성될 때마다 새로운 프로퍼티도 같이 생성
- 특정 타입에 속한 프로퍼티로 그 타입에 해당하는 하나의 프로퍼티만 생성
- 특정 타입의 모든 인스턴스에 공통으로 사용되는 값을 정의할 때 유용
- 인스턴스 프로퍼티와 다르게, 항상 초기값을 지정해서 사용해야함 ! 왜냐 타입 자체에는 초기자가 없어 초기화 할 곳이 없기 때문..
타입 프로퍼티 구문 (Type Property Syntax)
- 타입 프로퍼티 선언 : static 키워드로 선언
- 클래스에서 선언 : static , class(overriding 가능)로 타입 프로퍼티 선언
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
}
//enable to overriding
class var overrideableComputedTypeProperty: Int {
return 107
}
}
'Swift' 카테고리의 다른 글
Swift Concurrency 1 : async/await 시작하기 (동기/비동기 그리고 동시성 개념) (0) | 2024.08.11 |
---|---|
[Swift] 접근제어자 (Access Control) (0) | 2022.05.16 |
[Swift] inout parameter (0) | 2022.01.05 |
[Swift] 타입 캐스팅(Type Casting) - in / as 키워드 (0) | 2021.11.27 |
[Swift] Optinoal이란? Wrapping/Unwrapping (0) | 2021.10.02 |