목차
프로퍼티란?
프로퍼티는 클래스, 구조체, 열거형 등에 관련된 값을 뜻합니다.
저장 프로퍼티 (Store properties)
클래스 또는 구조체의 인스턴스와 연관된 값을 저장하는 가장 단순한 개념의 프로퍼티입니다.
저장 프로퍼티는 상수인 let 과 변수인 let 키워드를 사용하면 됩니다.
class Person {
// 저장 프로퍼티 선언
var name: String = "david"
var age: Int = 37
// 초기화 메서드 - 만약 초기값이 할당되어있다면 init을 생략해도 됩니다.
init(name: String, age: Int) {
self.name = name
self.age = age
}
// 메서드
func description() -> String {
return "\(name) is \(age) years old."
}
}
// Person 클래스의 인스턴스 생성
let person = Person(name: "Alice", age: 30)
print(person.description()) // 출력: Alice is 30 years old.
struct Car {
// 저장 프로퍼티 선언
var make: String
var model: String
var year: Int
// 초기화 메서드는 기본 제공되므로 생략 가능
// init(make: String, model: String, year: Int) {
// self.make = make
// self.model = model
// self.year = year
// }
// 메서드
func description() -> String {
return "\(year) \(make) \(model)"
}
}
// Car 구조체의 인스턴스 생성
let car = Car(make: "Toyota", model: "Camry", year: 2022)
print(car.description()) // 출력: 2022 Toyota Camry
구조체는 프로퍼티에 맞는 생성자(Initializer)를 제공하지만 클래스는 달리 저장 프로퍼티를 사용하는 것이 좀 번거롭지만
만약 미리 초기값을 설정해준다면, 생성자를 직접 구현해줄 필요는 없습니다.
지연 저장 프로퍼티 (Lazy stored properties)
인스턴스를 생성할 때 프로퍼티에 값이 필요 없다면 프로퍼티를 옵셔널로 선언해줄 수 있습니다. 조금 다른 용도로 지연 저장 프로퍼티 (Lazy stored properties) 가 있습니다. 지연 저장 프로퍼티는 호출이 있을 때만 값을 초기화 하고, 이때 lazy 키워드를 사용합니다.
class ComplexCalculator {
// 복잡한 계산을 수행하는 저장 프로퍼티를 lazy로 선언
lazy var result: Int = {
var sum = 0
for i in 1...1000000 {
sum += i
}
return sum
}()
init() {
print("ComplexCalculator initialized")
}
}
let calculator = ComplexCalculator()
print("Calculator created")
// 'result'에 처음 접근할 때 계산 수행
print("Result: \(calculator.result)")
예시 코드를 보시면 result에 접근 하고 나서야 변수가 초기화 되는 것을 볼 수 있습니다.
연산 프로퍼티 (Computed properties)
연산 프로퍼티는 실제 값을 저장하는 프로퍼티가 아니라, 특정 상태에 따른 값을 연산하는 프로퍼티입니다. 인스턴스 내/외부의 값을 연산하여 적절한 값을 돌려주는 접근자(getter)의 역할이나 은닉화된 내부의 프로퍼티 값을 간접적으로 설정하는 설정자(setter)의 역할을 할 수 도 있습니다.
연산 프로퍼티는 클래스, 구조체, 열거형에서 사용할 수 있습니다.
struct Rectangle {
var width: Double
var height: Double
// 연산 프로퍼티 area 정의
var area: Double {
get {
return width * height
}
set {
// 새로운 면적 값을 통해 너비를 설정
width = newValue / height
}
}
}
var rect = Rectangle(width: 10, height: 5)
print("Area: \(rect.area)") // 출력: Area: 50.0
rect.area = 100
print("New width: \(rect.width)") // 출력: New width: 20.0
예시 코드를 보시면 area가 변경됨 (100) 에 따라 set 매서드 내부에서 width를 변경하는 형태로 구현되어 있다는 것을 보실 수 있습니다.
또한 이미 rect 변수의 초기화 값인 width와 height 값이 바뀜에 따라 area값을 get했을때는 50.0 으로 받아온다는 것을 보실 수 있을 것입니다.
주의
설정자 (set) 매서드를 정의 하지 않았다면 읽기 전용으로 새로운 값을 설정한다면 오류가 발생합니다.
struct Circle {
var radius: Double
// 읽기 전용 연산 프로퍼티 circumference 정의
var circumference: Double {
get {
return 2 * .pi * radius
}
}
}
var circle = Circle(radius: 5)
print("Circumference: \(circle.circumference)") // 출력: Circumference: 31.41592653589793
// circumference는 읽기 전용이므로 값을 설정하려고 하면 에러가 발생함
circle.circumference = 40 // 컴파일 에러 발생
프로퍼티 감시자 (Property observers)
프로퍼티 감시자(Property Observers)는 프로퍼티 값이 변경될 때 호출되는 코드 블록입니다. Swift에서는 willSet과 didSet이라는 두 가지 프로퍼티 감시자를 제공합니다. willSet은 새로운 값이 설정되기 직전에 호출되고, didSet은 새로운 값이 설정된 후에 호출됩니다.
프로퍼티 감시자는 저장 프로퍼티에 대해 사용할 수 있으며, 연산 프로퍼티에는 사용할 수 없습니다. 프로퍼티 감시자를 사용하면 값의 변경을 감지하고 필요한 추가 작업을 수행할 수 있습니다.
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 = 100
// 출력:
// About to set totalSteps to 100
// Added 100 steps
stepCounter.totalSteps = 200
// 출력:
// About to set totalSteps to 200
// Added 100 steps
이 예시 코드에서는 totalSteps 값이 변경될 때마다 해당 값의 변경 사항을 출력하는 기능을 구현했습니다. willSet은 새로운 값이 설정되기 직전에 실행되고, didSet은 새로운 값이 설정된 후에 실행됩니다. 이를 통해 값이 변경될 때 필요한 추가 작업을 수행할 수 있습니다.
타입 프로퍼티 (Type properties)
타입 프로퍼티(Type Properties)는 특정 타입의 모든 인스턴스에서 공통으로 사용하는 프로퍼티입니다. 타입 프로퍼티는 클래스, 구조체, 또는 열거형에서 정의될 수 있습니다. 타입 프로퍼티는 저장 프로퍼티와 연산 프로퍼티 모두 가능하며, 저장 프로퍼티는 반드시 기본값을 지정해야 합니다. 타입 프로퍼티는 인스턴스가 생성되지 않아도 접근할 수 있습니다.
타입 프로퍼티는 static 키워드를 사용하여 정의하며, 클래스의 연산 타입 프로퍼티는 class 키워드를 사용할 수도 있습니다.
타입 프로퍼티의 예시
구조체에서의 타입 프로퍼티
struct SomeStruct {
// 저장 타입 프로퍼티
static var storedTypeProperty = "Some value."
// 연산 타입 프로퍼티
static var computedTypeProperty: Int {
return 1
}
}
print(SomeStruct.storedTypeProperty) // 출력: Some value.
print(SomeStruct.computedTypeProperty) // 출력: 1
클래스에서의 타입 프로퍼티
class SomeClass {
// 저장 타입 프로퍼티
static var storedTypeProperty = "Some value."
// 연산 타입 프로퍼티
static var computedTypeProperty: Int {
return 27
}
// 클래스에서의 연산 타입 프로퍼티 (서브클래스에서 재정의 가능)
class var overridableComputedTypeProperty: Int {
return 42
}
}
print(SomeClass.storedTypeProperty) // 출력: Some value.
print(SomeClass.computedTypeProperty) // 출력: 27
print(SomeClass.overridableComputedTypeProperty) // 출력: 42
타입 프로퍼티의 사용 예시
타입 프로퍼티는 모든 인스턴스에서 공유되는 데이터를 관리하는 데 유용합니다. 다음은 은행 계좌 클래스에서 전체 계좌의 고객 수를 관리하는 예제입니다.
class BankAccount {
// 저장 타입 프로퍼티
static var totalAccounts = 0
// 인스턴스 저장 프로퍼티
var accountNumber: Int
var balance: Double
init(accountNumber: Int, initialBalance: Double) {
self.accountNumber = accountNumber
self.balance = initialBalance
BankAccount.totalAccounts += 1
}
// 인스턴스 메서드
func deposit(amount: Double) {
balance += amount
}
func withdraw(amount: Double) {
balance -= amount
}
}
// 새로운 계좌를 생성할 때마다 totalAccounts 증가
let account1 = BankAccount(accountNumber: 12345, initialBalance: 1000.0)
let account2 = BankAccount(accountNumber: 67890, initialBalance: 500.0)
print("Total bank accounts: \(BankAccount.totalAccounts)") // 출력: Total bank accounts: 2
이 예제 코드에서는 BankAccount 클래스에 totalAccounts라는 저장 타입 프로퍼티를 정의하여, 생성된 모든 계좌의 총 수를 추적합니다. 각 인스턴스가 생성될 때마다 totalAccounts가 증가합니다.
타입 프로퍼티를 사용하면 특정 타입의 모든 인스턴스가 공통적으로 사용하는 데이터를 효율적으로 관리할 수 있습니다. 이를 통해 전체 인스턴스의 상태를 나타내거나 공유 데이터를 저장할 수 있습니다.
키 경로 (Keypath)
KeyPath에 대한 설명
Swift의 KeyPath는 타입 내의 특정 프로퍼티를 참조하는 방법을 제공하는 기능입니다. KeyPath를 사용하면 코드에서 프로퍼티를 간접적으로 접근하고 조작할 수 있습니다. 이는 주로 데이터 바인딩, 프로퍼티 관찰 및 표현식에서 유용합니다.
KeyPath는 다음과 같은 세 가지 유형이 있습니다:
KeyPath<Root, Value>: 읽기 전용 KeyPath
WritableKeyPath<Root, Value>: 읽기 및 쓰기 가능한 KeyPath
ReferenceWritableKeyPath<Root, Value>: 클래스 인스턴스에 대한 읽기 및 쓰기 가능한 KeyPath
KeyPath는 점(.) 구문을 사용하여 정의되며, 타입 안전하게 프로퍼티에 접근할 수 있게 해줍니다.
KeyPath 예시 코드입니다.
class Car {
var make: String
var model: String
init(make: String, model: String) {
self.make = make
self.model = model
}
}
let car = Car(make: "Toyota", model: "Camry")
// ReferenceWritableKeyPath 정의
let modelKeyPath = \Car.model
// KeyPath를 통해 값 읽기
let carModel = car[keyPath: modelKeyPath]
print("Car model: \(carModel)") // 출력: Car model: Camry
// KeyPath를 통해 값 쓰기
car[keyPath: modelKeyPath] = "Corolla"
print("Updated car model: \(car.model)") // 출력: Updated car model: Corolla
이 예제들을 통해 KeyPath가 어떻게 사용되는지 쉽게 이해할 수 있습니다. KeyPath는 프로퍼티에 간접적으로 접근할 수 있는 강력한 기능을 제공하며, 코드의 유연성과 재사용성을 높이는 데 유용합니다.
'iOS' 카테고리의 다른 글
[Swift] Struct(구조체)? Class(클래스)? (0) | 2024.06.15 |
---|---|
[SwiftUI] SwiftUI에서 탭바 숨기기 및 커스텀 백 버튼 만들기 (0) | 2024.06.13 |
[SwiftUI] SwiftData로 로컬에 관계형 데이터 만들어보기 (0) | 2023.12.20 |
[SwiftUI] 데이터 흐름: @Binding vs 단순 변수,상수 (0) | 2023.12.19 |
[Swift] 권한 요청 사용 + 구현하기 (feat. SwiftUI) (0) | 2023.12.01 |