Swift 5.1에 새로 도입된 Property Wrapper에 대해 정리하고자 합니다.
https://nshipster.com/propertywrapper/#auditing-property-access
를 바탕으로 정리한 내용입니다.
Property wrapper란?
반복적으로 나타나는 property 구현 패턴에 대한 집합을 컴파일러에 하드코딩하는 대신, 이러한 패턴을 라이브러리로 정의할 수 있는 일반적인 메커니즘을 제공하는 것이다.
@키워드를 사용하며, Swift UI에서 @State, @Binding, @EnvironmentObject 등의 많은 Property wrapper를 사용하는 것으로 보아 앞으로는 @키워드가 활성화 될 것이라고 생각한다.
언제 사용하면 좋을까?
- Constraining Values : 제약 값
- Transforming Values on Property Assignment : 속성 할당과 변환
- Changing Synthesized Equality and Comparision Semantics: 합성 동등 및 비교 의미 변경
- Auditing Property Access : 속성 접근 감시
첫 번째 항목 부터 살펴보자.
[Constraining Values : 제약 값]
값을 규제하는 것을 통해 보장할 수 있다.
Related Ideas
- 음수 양수가 아닌 정수임을 보장하는 @Positive / @NonNegative
- 숫자 값이 0이 아님을 보장하는 @NonZero
- 할당할 수 있는 값을 제한하는 @Validated 또는 @Whitelisted / @Blacklisted
[Transforming Values on Property Assignment : 속성 할당과 변환]
문자열 공백 처리 등과 같은 변환시 사용할 수 있다.
위와 같이 공백이 있을 경우를 처리하고 싶다면?
위 방법으로는 해결하기가 수월하지 않다.. 이는 Property Wrapper를 통해 적절하게 해결할 수 있다.
Related Ideas
- 문자열에 ICU transforms을 적용하는 @Transformed
- 사용자 정의 normalization form을 제공하는 @Normalized
- 가장 가까운 1/2 지점까지 반올림하는 것처럼 중간값을 추적하는 @Quantized, @Rounded, @Trancated
[Changing Synthesized Eqaulity and Comparison Semantics : 합성 동등 및 비교 의미 변경]
대소문자를 구별하지 않는 문자열 비교처럼 다른 equality semantics를 원하는 경우에 적합하다.
기존 방안
- 항상 lowercased()의 결과를 이용해서 비교하는 방법.
// 이런 수동 작업은 버그를 만들 가능성이 크다. - CaseInsensitive처럼 새로운 타입을 정의하고 문자열 값을 래핑하는 방법.
// 하지만 기능적인 추가 작업이 많이 들어간다. - custom comparator를 만드는 방법.
// 하지만 == 연산자보다 명확한 것은 없다.
Property Wrapper를 사용하여 매력적으로 해결해보자.
Account 객체는 name 프로퍼티를 대소문자를 구분하지 않는 비교를 통해 동일성 검사할 수 있으며 name 프로퍼티를 가져오거나 설정하려고 하면 진정한 의미의 String으로 사용할 수 있다.
Swift 4 이후 컴파일러는 특정 타입의 모든 저장 프로퍼티가 Equatable을 따르고, 해당 타입 또한 Equatable를 선언하면 그 타입에 대해선 자동으로 Equatable를 준수하도록 설정된다.
Related Ideas
- "①"과 "1" 처럼 래핑된 문자열을 동일한 것으로 간주하는 @CompatibilityEquivalence
- 부동소수점 타입에 대한 equality semantic을 정제하는 @Approximate
- 카드놀이의 .ace를 상황에 따라 가장 높거나 가장 낮은 카드로 취급하는 것처럼, 엄격한 순서를 정의하는 기능을 하는 @Ranked
[Auditing Property Access : 속성 접근 감시]
누가 언제 어떤 기록에 접근할 수 있는지와 같은 특정 동작을 규정하거나 시간에 따른 form의 변경 사항을 규정하는 것이 비지니스 요구사항일 수 있다.
일반적으로 앱에서 수행하는 동작은 아니다. 대부분 비지니스 로직은 서버에 정의되어 있고, 대부분 그게 맞다고 생각한다. 하지만 이 경우는 Property Wrapper의 관점으로 봤을 때, 매력적인 use case가 될 수 있다.
Related Ideas
- 값을 읽거나 쓸 때마다 시간을 기록하는 @Audited
- 값을 읽을 때마다 설정된 숫자 값을 나누는 @Decaying
Property Wrapper Limitations
1. 에러 핸들링에 적합하지 않다.
속성에는 getter / setter가 모두 있으므로 오류처리를 추가할 경우, 올바른 설계가 무엇인지 알 수 없다.
해결법... 1) 조용히 무시하기... 2) fatalError()로 표기...
f.e. @Versioned 프로퍼티 래퍼를 확장해서, 이전에 denied 였던 적이 있다면, 후에 .approved 상태가 될 수 없도록 하고 싶을 경우, fatalError()를 사용할 수 있는데, 이는 앱에 적합하지 않다.
2. 별칭(aliased)으로 사용할 수 없다.
많은 코드 복제를 일으킬 여지가 있다. 잘못된 추상화를 피하기 위해 어느정도는 감수할 수 있다고 생각한다.
3. 구성하기 까다롭다.
Property Wrapper의 조합은 상호적인 동작이 아니라 선언하는 순서에 따라 동작의 차이를 보인다.
4. 종속 유형이 아니다.
종속 타입은 값에 따라 타입이 결정되는 것을 의미한다.
f.e. 후자가 전자에 비해 큰 정수 쌍, 소수를 가진 배열 = 값에 따라 타입 정의가 달라지므로 종속 타입
→ Swift가 종속 유형에 대한 자원이 부족한 것은 이러한 보증이 런타임에 시행되어야 한다는 것을 의미한다.
f.e Property Wrapper를 사용해서 값이 가능한 제약조건이 있는 새로운 타입을 정의할 수 없다.
또한 Property Wrapper를 사용해서 Collection의 Key 또는 Value를 설정할 수 없다.
'Swift' 카테고리의 다른 글
[Swift] Understanding Swift Performance - Class와 Protocol (0) | 2020.03.09 |
---|---|
[Swift] What's New in Swift (5.0/5.1) (1) | 2020.02.29 |