티스토리 뷰
[DI] DI Container, IOC Container 개념과 예제 에서 간단하게 Property Wrapper 를 사용했었는데요,
Swift Docs > Properties > Property Wrappers 에서 더 자세한 내용들을 알게 되어서 정리합니다 ✏️
# Property Wrapper 전제조건
Property Wrapper 는 Swift 5.1 에 추가되었고
local stored variable 에만 사용가능합니다 (global variable 또는 computed variable 에서 사용불가)
# Property Wrapper 정의하기
Property Wrapper 를 정의하려면 wrappedValue property 를 정의한 structure, enumeration, class 를 만들면 됩니다.
아래 코드에서 TwelveOrLess structure 는 래핑되는 값이 항상 12 이하 이도록 보장합니다.
(number가 TwelveOrLess 구현내에서만 사용되도록 private로 선언되어있습니다)
@propertyWrapper
struct TwelveOrLess {
private var number = 0
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
# Property Wrapper 사용하기
property name 앞에 wrapper name을 적어줌으로써 wrapper를 적용할 수 있습니다.
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"
height와 width 프로퍼티는 TwelveOrLess에서 초기값을 가져옵니다. (number가 0)
# Property Wrapper 필요성
Property Wrapper를 안쓰면 위의 코드는 이렇게 구현되어야합니다.
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 }
}
}
이렇게 여러 프로퍼티들에 동일한 관리 코드를 작성해줘야하는 경우, 프로퍼티 래퍼를 유용하게 사용할 수 있습니다.
프로퍼티 래퍼를 정의해서 관리 코드를 한번만 작성해준 후, 여러 프로퍼티들에 프로퍼티 래퍼를 적용하여
관리 코드를 재사용할 수 있기 때문입니다.
영어가 더 명확한 것 같아서 첨부!
When you use a property wrapper, you write the management code once when you define the wrapper, and
then reuse that management code by applying it to multiple properties.
# Wrapped Property 초기값 설정
위에서 살펴본 코드는 wrapped property 에 대한 initial value 를 TweetwOrLess 에 정의된 값으로 세팅하고 있습니다.
다른 initial value 를 사용할 수 없는 상황이죠!
초기값 설정을 지원하고 싶으면 property wrapper 에 이니셜라이저를 추가해줘야합니다.
@propertyWrapper
struct TwelveOrLess {
private var number = 0
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
init(wrappedValue: Int) {
number = min(wrappedValue, 12)
}
}
struct SmallRectangle {
@TwelveOrLess var height: Int = 2
@TwelveOrLess var width: Int = 200
}
var rectangle = SmallRectangle()
print(rectangle.height)
// Prints "2"
print(rectangle.width)
// Prints "12"
이렇게 바꾸면 아래와 같이 initial value 를 명시하지 않을 때 컴파일 에러가 납니다.
@TwelveOrLess var width: Int
이런 경우도 대응해주기 위해, init() 이니셜라이저를 추가해줍니다.
@propertyWrapper
struct TwelveOrLess {
private var number = 0
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
init() {
}
init(wrappedValue: Int) {
number = min(wrappedValue, 12)
}
}
struct SmallRectangle {
@TwelveOrLess var height: Int = 2
@TwelveOrLess var width: Int
}
var rectangle = SmallRectangle()
print(rectangle.height)
// Prints "2"
print(rectangle.width)
// Prints "0"
➕
여기서 더 나아가서 maximum 값도 설정할 수 있도록 해봅시다.
네이밍도 TwelveOrLess 에서 SmallNumber로 바꿔주겠습니다.
SmallNumber 는 세개의 이니셜라이저를 가지고 있기 때문에 다양하게 사용될 수 있습니다.
@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)
}
1)
// Swift uses the init() initializer
struct ZeroRectangle {
@SmallNumber var height: Int
@SmallNumber var width: Int
}
var zeroRectangle = ZeroRectangle()
print(zeroRectangle.height, zeroRectangle.width)
// Prints "0 0"
2)
// Swift uses the init(wrappedValue:) initializer to set up the wrapper.
struct UnitRectangle {
@SmallNumber var height: Int = 1
@SmallNumber var width: Int = 1
}
var unitRectangle = UnitRectangle()
print(unitRectangle.height, unitRectangle.width)
// Prints "1 1"
3)
// Swift uses the init(wrappedValue:maximum:) initializer
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"
3번처럼 property wrapper에 argument를 포함하는 방식이 가장 일반적인 방식이라고 합니다!
그리고 argument를 포함하는 경우에도 assignment를 통해 initial value를 할당해줄 수 있는데,
Swift는 이를 wrappedValue argument 로 여긴다고 합니다.
예를들어 아래와 같은 경우에 SmallNumber(wrappedValue: 3, maximum: 4) 를 콜합니다.
@SmallNumber(maximum: 4) var width: Int = 3
# Projected Value
property wrapper는 wrapped value 뿐만아니라 projected value를 추가하여 기능을 확장할 수 있습니다.
예를들어 projected value 를 통해 property wrapper가 저장하기 전에 값을 조정(adjust) 했는 지 알려줄 수 있습니다.
@propertyWrapper
struct SmallNumber {
private var number: Int
private(set) var projectedValue: Bool
var wrappedValue: Int {
get { return number }
set {
if newValue > 12 {
number = 12
projectedValue = true
} else {
number = newValue
projectedValue = false
}
}
}
init() {
self.number = 0
self.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"
projectedValue 프로퍼티를 property wrapper에 추가하면 (꼭 이 네이밍 이여야합니다!!)
private(set) var projectedValue: Bool
property wrapper를 사용하는 쪽에서 dollar sign ($) 으로 접근할 수 있는 프로퍼티가 자동으로 생깁니다.
$someNumber 를 통해 wrapper의 projected value 에 접근하여
내가 세팅해준 값이 property wrapper에 의해 조정되었는 지 여부를 알 수 있습니다.
projected value 는 어떤 타입이던지 가능합니다.
위의 예제에서는 하나의 정보(whether the number was adjusted)만 필요해서 bool 값으로만 정의했지만,
더 많은 정보를 노출해야하는 wrapper인 경우, 다른 데이터 타입의 instance를 리턴하거나 self를 리턴할 수 도 있습니다.
'🍏 > Swift' 카테고리의 다른 글
[Swift] Protocol Composition > One Class Type 포함가능 (0) | 2022.01.18 |
---|---|
[Swift] Structure > custom initializer를 추가해도 default, memberwise initializer 를 유지하는 방법 (0) | 2022.01.08 |
[Swift] Autoclosure 개념과 활용사례 (0) | 2021.12.30 |
[Swift] 함수 관련 혼용되는 용어 정리 (parameter name, argument label, argument value) (0) | 2021.12.26 |
[Swift] Concurrency (2) | 2021.12.14 |
- Total
- Today
- Yesterday
- Flutter Clipboard
- Dart Factory
- flutter deep link
- Flutter 로딩
- Sketch 누끼
- flutter dynamic link
- SerializerMethodField
- flutter 앱 출시
- github actions
- ribs
- Django Heroku Scheduler
- Flutter Text Gradient
- Python Type Hint
- METAL
- 장고 Custom Management Command
- Watch App for iOS App vs Watch App
- 플러터 얼럿
- drf custom error
- 구글 Geocoding API
- DRF APIException
- 장고 URL querystring
- cocoapod
- Flutter getter setter
- Flutter Spacer
- 플러터 싱글톤
- ipad multitasking
- PencilKit
- Django FCM
- flutter build mode
- Django Firebase Cloud Messaging
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |