[Swift] Properties(3)

상 원·2022년 7월 13일
0

Swift

목록 보기
17/31
post-thumbnail

Property Wrappers

프로퍼티가 저장되는 방법과 그 프로퍼티가 정의되는 코드를 분리하는 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처럼 선언해도 같은 역할을 해 줌!

Wrapped Value의 초기값 설정하기

위에서의 예제들은 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:) 요거 사용함!

이런식으로 이니셜라이저를 사용해서 프로퍼티의 초기값을 지정해 줄 수 있다. 이니셜라이저에 대한 자세한 건 다음에 공부해보장.

profile
ios developer

0개의 댓글