간단하게 property는 member 변수이다. 그런데 Swift에는 다양한 종류의 property가 존재한다. 사실 어떻게 보면 활용방법? 테크닉에 가깝겠다. 어떻게 property를 관리하는 지 안다면, 실제 코드를 간결하고 읽기 좋게 유지할 수 있다. 그럼 출발해보자.
var
로만 선언 가능get
블록을 작성위의 4가지가 섞여서 존재할 수 있다. 즉 4가지의 가능성이 존재.
get
블럭과 set
블럭 모두 구현get
만 구현set
에서 암시적 매개변수 newValue를 사용할 수 있음struct Student {
// 인스턴스 저장 프로퍼티
var name: String = ""
var `class`: String = "Swift"
var koreanAge: Int = 0
// 인스턴스 연산 프로퍼티
var westernAge: Int {
get {
return koreanAge - 1
}
set(inputValue) {
koreanAge = inputValue + 1
}
}
// 타입 저장 프로퍼티
static var typeDescription: String = "학생"
// 읽기 전용 타입 연산 프로퍼티
// 읽기 전용이 default라 적지 않으면 읽기전용이라고 판단함
static var selfintroduction: String {
return "학생타입입니다."
}
}
// 인스턴스 생성
var wansik: Student = Student()
wansik.name = "wansik"
wansik.koreanAge = 20
// 인스턴스 저장 프로퍼티 사용
print(wansik.name) // wansik
// 인스턴스 연산 프로퍼티 사용
print(wansik.westernAge) // 19
// 타입 저장 프로퍼티 사용
print(Student.typeDescription) // 학생
// 타입 연산 프로퍼티 사용
print(Student.selfintroduction) // "학생타입입니다."
willSet
블럭에서 암시적 매개변수 newValue
를 사용 가능didSet
블럭에서는 oldValue
를 사용 가능struct Money {
// 프로퍼티 감시자 사용
var currencyRate: Double = 1100 {
willSet(newRate) {
print("환율이 \(currencyRate)에서 \(newRate)으로 변경될 예정입니다")
}
didSet(oldRate) {
print("환율이 \(oldRate)에서 \(currencyRate)으로 변경되었습니다")
}
}
// 매개 변수를 암시적으로 newValue, oldValue를 갖는다.
var dollar: Double = 0 {
willSet {
print("\(dollar)달러에서 \(newValue)달러로 변경될 예정입니다")
}
didSet {
print("\(oldValue)달러에서 \(dollar)달러로 변경되었습니다")
}
}
// 연산 프로퍼티
var won: Double {
get {
return dollar * currencyRate
}
set {
dollar = newValue / currencyRate
}
// get, set과 함께 사용이 불가함
// willSet {
// print("원화의 값이 \(won)으로 변경될 예정입니다.")
// }
// didSet {
// print("원화의 값이 \(won)으로 변경되었습니다.")
// }
}
}
var moneyInMyPocket = Money()
// 환율이 1100.0에서 1150.0으로 변경될 예정입니다
moneyInMyPocket.currencyRate = 1150.0
// 환율이 1100.0에서 1150.0으로 변경되었습니다
//0.0달러에서 10.0달러로 변경될 예정입니다
moneyInMyPocket.won = 11500
//0.0달러에서 10.0달러로 변경되었습니다
print(moneyInMyPocket.dollar) // 10
property 저장 방법 코드와 property 관리 코드를 구분
property set, get할 때 공통적으로 적용되는 반복코드를 관리할 수 있음
struct, enum, class 선언 앞에 @propertyWrapper
를 붙이고 wrappedValue
정의하면 됨
특정 데이터의 boundary를 정할 수 있음
@propertyWrapper struct TwelveOrLess {
private var number = 0
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
struct SmallRectangle {
@TwelveOrLess var height: Int
@TwelveOrLess var width: Int
}
Initializer를 통해 value 초기화 가능
@propertyWrapper struct SmallNumber {
private var maximum: Int
private var number: Int
var wrappedValue: Int {
get { return number }
set { number = min(newValue, maximum) }
}
init() {
self.maximum = 12
self.number = 0
}
init(maximum: Int) {
self.maximum = maximum
self.number = 0
}
init(wrappedValue: Int, maximum: Int) {
self.maximum = maximum
self.number = min(wrappedValue, maximum)
}
}
struct ZeroRectangle {
@SmallNumber(wrappedValue: 5, maximum: 10) var height: Int // custom initializer를 사용한 초기화
@SmallNumber(maximum: 10) var width: Int = 5 // 값 할당과 custom initializer를 섞어서 사용함
}
Projected Value
projectValue
를 사용하면 wrappedValue
를 다른 방법으로 드러낼 수 있음
$
를 통해 접근이 가능함
@propertyWrapper struct SmallNumber {
private var number = 0
var projectedValue = false
var wrappedValue: Int {
get { return number }
set {
if newValue > 12 {
number = 12
projectedValue = true
} else {
number = newValue
projectedValue = false
}
}
}
}
struct SizedRectangle {
@SmallNumber var height: Int
@SmallNumber var width: Int
mutating func resize(to size: Size) -> Bool {
switch size {
case .small:
height = 10
width = 20
case .large:
height = 100
width = 100
}
return $height || $width // True or False || True or False
}
}