언제나 숫자 12 이하로 값을 저장해주는 property wrapper 만들어보기
@propertyWrapper
struct TwelveOrLess {
private var number = 0
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
Swift
복사
setter 는 새로 들어온 값이 12 이하로 저장되도록 하고, getter 는 저장된 값을 그대로 리턴.
Note number는 private 변수로 선언되었기 때문에 TwelveOrLess 내부에서만 사용가능하다. 외부에서는 number에 바로 접근이 불가능하고, getter/setter 를 통해서만 접근 가능하다.
12 이하의 변 길이를 가진 작은 직사각형
struct SmallRectangle {
@TwelveOrLess var height: Int
@TwelveOrLess var width: Int
}
var rectangle = SmallRectangle()
print(rectangle.height)
// Prints "0"
rectangle.height = 10
print(rectangle.height)
// Prints "10"
rectangle.height = 24
print(rectangle.height)
// Prints "12"
Swift
복사
TwelveOrLess 의 확장 버전인 SmallNumber
: TwelveOrLess 에는 정의되지 않은 wrappedValue(초기값)와 max값을 설정할 수 있는 이니셜라이저가 정의되어있음
@propertyWrapper
struct SmallNumber {
private var maximum: Int
private var number: Int
var wrappedValue: Int {
get { return number }
set { number = min(newValue, maximum) }
}
init() {
maximum = 12
number = 0
}
init(wrappedValue: Int) {
maximum = 12
number = min(wrappedValue, maximum)
}
init(wrappedValue: Int, maximum: Int) {
self.maximum = maximum
number = min(wrappedValue, maximum)
}
}
Swift
복사
초기값을 주지 않았을 때
@propertyWrapper
struct SmallNumber {
private var maximum: Int
private var number: Int
var wrappedValue: Int {
get { return number }
set { number = min(newValue, maximum) }
}
init() {
maximum = 12
number = 0
}
init(wrappedValue: Int) {
maximum = 12
number = min(wrappedValue, maximum)
}
init(wrappedValue: Int, maximum: Int) {
self.maximum = maximum
number = min(wrappedValue, maximum)
}
}
Swift
복사
struct ZeroRectangle {
@SmallNumber var height: Int
@SmallNumber var width: Int
}
var zeroRectangle = ZeroRectangle()
print(zeroRectangle.height, zeroRectangle.width)
// Prints "0 0"
Swift
복사
프로퍼티의 초기값(wrappedValue)을 따로 명시하는 경우
struct UnitRectangle {
@SmallNumber var height: Int = 1
@SmallNumber var width: Int = 1
}
var unitRectangle = UnitRectangle()
print(unitRectangle.height, unitRectangle.width)
// Prints "1 1"
Swift
복사
프로퍼티 선언 시 커스텀 attribute 뒤에 괄호를 열어 인자들을 넘기는 경우
struct NarrowRectangle {
@SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
@SmallNumber(wrappedValue: 3, maximum: 4) var width: Int
}
var narrowRectangle = NarrowRectangle()
print(narrowRectangle.height, narrowRectangle.width)
// Prints "2 3"
narrowRectangle.height = 100
narrowRectangle.width = 100
print(narrowRectangle.height, narrowRectangle.width)
// Prints "5 4"
Swift
복사
ProjectedValue 이해하기
위의 SmallNumber 예제에서는 프로퍼티에 너무 큰 수를 할당하려 시도할 경우 property wrapper가 자체적으로 조정을 거쳐서 값을 저장한다.(max값보다 클 경우 max값을 셋팅).
이렇게 새로운 값을 저장하기 전에 값의 조정이 일어났는지의 여부를 기록
@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 SomeStructure {
@SmallNumber var someNumber: Int
}
var someStructure = SomeStructure()
someStructure.someNumber = 4
print(someStructure.$someNumber)
// Prints "false"
someStructure.someNumber = 55
print(someStructure.$someNumber)
// Prints "true"
Swift
복사
projectedValue에 접근하기 위해서는 프로퍼티 이름 앞에 달러 기호($)를 붙여야한다.
이번 과제 요약
•
프로퍼티의 getter/setter에 들어갈 코드가 여러 프로퍼티에서 재사용되는 경우, 그것을 Property wrapper로 정의해놓으면 쉽게 그 동작을 끌어다 쓸수 있다. (프로퍼티의 저장부-getter/setter 동작이 정의된 코드와 정의부-실제로 프로퍼티를 사용하는 코드를 분리하고 싶을때도 유용)
•
Property Wrapper를 정의하는 방법: 구조체, ENUM, 클래스 등을 만들고 @propertyWrapper라고 명시해주기. 실제 저장값은 wrappedValue로 정의, 추가로 제공하고 싶은 정보가 있다면 projectedValue를 정의할 것
•
Property Wrapper를 밖에서 가져다쓰는 방법: 나의 프로퍼티를 정의하고 @프로퍼티래퍼명으로 attribute를 붙인다. 그러면 이 프로퍼티는 get/set동작이 이루어질때마다 알아서 Property wapper의 getter/setter 코드를 통해 실행됨