Property Wrapper 그게 왜 필요한데
swift에서는 struct, calss의 프로퍼티의 종류가 두 가지이다. 저장 프로퍼티 vs 연산 프로퍼티 로 구분된다.
이때 연산 프로퍼티는 값에 접근할 때 계산을 하거나 변환 작업을 해야할 때 사용된다. 즉 값을 검사하는 수문장이 있는 프로퍼티이다.
예를 들어, 다음과 같이 값의 최대 범위를 계산하여 막아주는 연산 프로퍼티가 있다고 생각하자.
struct A
{
private var realValue: Int = 0
var value: Int {
get {
return realValue
}
set {
if (realValue > 100) {
realValue = 100
}
else if (realValue < 0) {
realValue = 0
}
else {
realValue = newValue
}
}
}
}
struct B
{
private var realValue: Double = 0
var value: Double {
get {
return realValue
}
set {
if (realValue > 100.0) {
realValue = 100
}
else if (realValue < 0) {
realValue = 0
}
else {
realValue = newValue
}
}
}
}
struct C
{
private var realValue: String = ""
var value: String {
get {
return realValue
}
set {
if (realValue > "z") {
realValue = "z"
}
else if (realValue < "a") {
realValue = "a"
}
else {
realValue = newValue
}
}
}
}
분명 구조체는 3개밖에 없는데 코드는 엄청나게 길어졌다. 심지어 로직도 똑같은데 반복되는 비효율적인 코드이다.
그럼 어떻게 코드의 길이를 줄여볼까?
혹시 연산 프로퍼티를 계산하는 부분만 따로 모듈화를 진행할 수 없을까?
이 물음에서 나온것이 swift 5.1에서 출시된 property wrapper이다.
Property Wrapper 사용하기
앞에서 말했듯이 property wrapper는 연산 프로퍼티의 기능을 개별 클래스와 구조체로 분리할 수 있고, 재사용이 가능하다는 점에서 효율을 제공한다.
@propertyWrapper 를 사용하여 정의한다.
위 코드에서 보여진 연산 프로퍼티를 property wrapper를 사용하여 모듈화를 진행하자.
이때 사용되는 지시자가 @propertyWrapper 이다.
@propertyWrapper
struct isValid<V: Comparable>
{
var value: V
var min: V
var max: V
init(wrappedValue: V, min: V, max: V) {
self.value = wrappedValue
self.min = min
self.max = max
}
var wrappedValue: V {
get {
return value
}
set {
if (value > self.max) {
value = self.max
}
else if (value < self.min) {
value = self.min
}
else {
value = newValue
}
}
}
}
위에서 사용된 구조체에서 저장되는 값의 타입은 Int, Double, String 으로 다양하다.
이럴때는 generic 을 사용한다.
연산 프로퍼티는 기본적으로 값의 비교, 연산을 하는 것이 목적이다. 즉 들어올 수 있는 값은 Comparable 프로토콜을 따르는 모든 데이터 타입을 가질 수 있도록 데이터 타입을 generic으로 선언해둔다.
property wrapper는 값을 검사하는 수문장 역할을 하는 wrappedValue property가 있어야 하며, 이름이 변경되어서는 안된다. 이는 init() 초기화 함수에서도 마찬가지로, 파리미터에 wrappedValue를 쓸 때 이름이 변경되어서는 안된다.
이렇게 선언한 property wrapper는 다음과 같이 property 앞에 @propertywrappername 을 붙여서 사용한다.
struct A
{
@isValid(wrappedValue: 150, min: 100, max: 200) var value: Int
}
struct B
{
@isValid(wrappedValue: 145.6, min: 100.0, max: 200.0) var value: Double
}
struct C
{
@isValid(wrappedValue: "a", min: "a", max: "d") var value: String
}
그렇다면 값을 변경할 때는 어떻게 작동할까?
다음과 같이 구조체를 할당하고 value 의 값을 범위를 벗어나게 바꿔보았다. 이때 값을 변경하기 때문에 var로 구조체를 선언한다.
var a = A()
a.value = 19392342
print(a.value)
var b = B()
b.value = 124935.2342
print(b.value)
var c = C()
c.value = "zzzz"
print(c.value)
결과는 당연히 다음과 같다.
연산 프로퍼티가 하던 계산을 wrapped property로 사용하는 구조체에서 하고 이를 저장한다.
'MOBILE > ios' 카테고리의 다른 글
[iOS] UNUserNotificationCenterDelegate 로 보낸 로컬 알람 처리하기 (0) | 2022.04.08 |
---|---|
[iOS] UserNotification으로 로컬 알림 사용하기 (0) | 2022.04.06 |
[iOS] URLSession을 사용하여 POST method 통신하기 (0) | 2022.04.01 |
[iOS] iOS의 UIApplicationDelegate의 life cycle method (0) | 2022.03.29 |
[iOS] segue 객체를 이용한 화면 전환 처리하기 (0) | 2022.03.27 |