프로퍼티가 저장되는 방법과 그 프로퍼티가 정의되는 코드를 분리하는 layer를 만들어줌.
무슨 말인지 잘 모르겠지만.. 예제를 보면 대충 프로퍼티에 대한 성질? 로직? 을 재사용할 수 있는 방법 중 하나인 것 같다.
예를 들어 thread-safety를 체크하는 코드를 프로퍼티마다 적용하고 싶다면, 모든 프로퍼티에 똑같은 코드를 써야함.
근데 property wrapper
를 쓰면 wrapper
정의할 때 한번만 쓰고 나머지 프로퍼티에는 이 management code
를 재사용해서 붙여주기만 하면 되는거!
예제를 한번 봅시다.
// wrapper를 정의하는 코드
@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
}
var rectangle = SmallRectangle()
print(rectangle.height)
// Prints "0"
rectangle.height = 10
print(rectangle.height)
// Prints "10"
rectangle.height = 24
print(rectangle.height)
// Prints "12"
property wrapper
를 정의하려면 wrappedValue
를 정의하는 구조체나 클래스, 열거형을 만들어주면 된다.
위에서 만든 구조체는 wrappedValue가 12 이하의 값을 갖게 해줌. 들어오는 값을 비교해서 12보다 크면 12로 설정하고, 작으면 그 값 그대로 설정하는거.
그러면 이 프로퍼티 래퍼를 사용해서 각 프로퍼티에 속성을 집어넣을 수 있음! SmallRectangle 구조체에서 선언한 height, width 프로퍼티에 TwelveOrLess 라는 속성을 더해줄 수 있는거!!
그러면 height, width도 wrappedValue가 가진 성질, 그니까 12 이하의 수를 저장하는 성질을 가지게 된다. 그래서 프로퍼티 래퍼라고 불리는 듯??
그리고 위에서는 @TwelveOrLess 라는 특수구문을 사용해 프로퍼티 래퍼를 사용했지만, 다음 예제처럼 인스턴스를 만들어서 사용할 수도 있음!
struct SmallRectangle {
private var _height = TwelveOrLess()
private var _width = TwelveOrLess()
var height: Int {
get { return _height.wrappedValue }
set { _height.wrappedValue = newValue }
}
var width: Int {
get { return _width.wrappedValue }
set { _width.wrappedValue = newValue }
}
}
이렇게 _height, _width처럼 선언해도 같은 역할을 해 줌!
위에서의 예제들은 number의 초기값을 Property Wrapper에서 선언해 줬기 때문에 이걸 사용하는 프로퍼티들에서는 따로 토기값을 정할 수가 옶다!
그래서 이걸 프로퍼티마다 초기값을 넣어주고 싶다면 프로퍼티 래퍼 안에 initializer 를 추가해줘야 함!!!
@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)
}
}
요 init() 에 대해서는 나중에 나오지만 일단..
이 프로퍼티 래퍼를 사용할 때 각 변수들에 대해 딱히 초기값을 쓰지 않으면 init() 이 자동으로 써짐!
struct ZeroRectangle {
@SmallNumber var height: Int
@SmallNumber var width: Int
}
var zeroRectangle = ZeroRectangle()
print(zeroRectangle.height, zeroRectangle.width)
// Prints "0 0"
이렇게 ㅇㅇ
근데 이렇게 초기값을 설정했다면?
struct UnitRectangle {
@SmallNumber var height: Int = 1
@SmallNumber var width: Int = 1
}
var unitRectangle = UnitRectangle()
print(unitRectangle.height, unitRectangle.width)
// Prints "1 1"
init(wrappedValue:) 이게 실행되게됨ㅇㅇ
또 만약에
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"
이렇게 매개변수로 값을 전달하면?
전달된 매개변수를 포함하는 initializer를 실행해준다.
init(wrappedValue:maximum:) 요거 사용함!
이런식으로 이니셜라이저를 사용해서 프로퍼티의 초기값을 지정해 줄 수 있다. 이니셜라이저에 대한 자세한 건 다음에 공부해보장.