Search

Property Wrapper (조민식)

언제나 숫자 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 코드를 통해 실행됨