티스토리 뷰
안녕하세요 :) Zedd입니다.
오랜만에 노래추천!
저스틴 비버 신곡! 뮤비보면서 눈물 줄줄..🥲
암튼 들으면서 dynamicMemberLookup정리
dynamicMemberLookup
[특징]
✔️ SE-0195 에서 처음 소개. Swift 4.2에서 구현됨. (참고 Swift 4.2 변경사항)
✔️ class, struct, enum, protocol에 사용 가능
✔️ subscript (dynamicMemberLookup :) 메소드를 구현해야함.
dynamic / Member / Lookup
직역하자면 동적 / 멤버 / 조회인데..
동적으로 멤버들을 조회할 수 있게 만드는 듯한..그런 느낌이 옵니다.
1. 만약 dynamicMemberLookup을 "사용"하는 타입이 있다면,
2. 이 타입은 런타임시 확인되는 임의의 "name"에 대해
3. "dot" syntax를 사용할 수 있도록 제공해준다.
도대체 이게 몬 소리야
# Subscripts
혹시 Swift의 Subscripts에 대해서 아시나요? 직역하자면 "첨자"라고 이야기 할 수 있습니다.
이 첨자를 사용하여 index로 값을 설정하고, 값을 가져올 수 있습니다.
var arr = ["Zedd", "Alan Walker"]
arr[0] // Zedd
arr[1] = "Marshmello"
우리에게 너무나도 익숙한 문법입니다.
이것이 가능한 이유는, Swift의 Collection이 subscript를 요구하고 있기 때문입니다.
public protocol Collection: Sequence {
...
subscript(position: Index) -> Element { get }
}
배열, Dictionary, Set같은 친구들은 Collection 프로토콜을 준수하고 있기 때문에, Subscripts로 접근이 가능한 것입니다.
하지만
struct Contact {
var persons: [String: String]
}
let contact = Contact(persons: [
"지구": "Zedd",
"달": "Marshmello"
])
let zedd = contact["지구"] // ❌ Error! Value of type 'Contact' has no subscripts
제가 Contact라는 struct를 정의하고, 사는 곳 이름이 담긴 Dictionary를 persons에 넣어줬습니다.
그리고 contact[사는 곳] 이렇게 접근하려고 하지만, 에러가 나는 것을 볼 수 있습니다.
Value of type 'Contact' has no subscripts
이때 subscript를 타입 내부에 구현해주면 됩니다.
struct Contact {
var persons: [String: String]
subscript(planet: String) -> String? {
return self.persons[planet]
}
}
let contact = Contact(persons: [
"지구": "Zedd",
"달": "Marshmello"
])
let zedd = contact["지구"] // Zedd
이렇게요.
위 배열에서 봤던 예제로 동일하게 subscript로 set연산도 가능합니다.
subscript(index: Int) -> Int {
get {
// Return an appropriate subscript value here.
}
set(newValue) {
// Perform a suitable setting action here.
}
}
subscript는 연산 프로퍼티와 동일하게..get-only면 get구문을 생략할 수 있거든요. set역시 존재합니다.
Contact에 적용시켜볼게요.
struct Contact {
var persons: [String: String]
subscript(planet: String) -> String? {
get {
return self.persons[planet] ✅
} set {
self.persons[planet] = newValue ✅
}
}
}
var contact = Contact(persons: [
"지구": "Zedd",
"달": "Marshmello"
])
var zedd = contact["지구"] // Zedd
contact["지구"] = "Alan Walker"
let walker = contact["지구"] // "Alan Walker"
이렇게 할 수 있습니다.
아니 그런데...dynamicMemberLookup이야기 하다가 갑자기 subscript이야기는 너무 뜬금스럽나요?
dynamicMemberLookup이 subscript와 유사하여 아주 간단하게 살펴봤어요.
그렇다면 dynamicMemberLookup를 사용해보겠습니다.
# dynamicMemberLookup
1. 만약 dynamicMemberLookup을 "사용"하는 타입이 있다면,
2. 이 타입은 런타임시 확인되는 임의의 "name"에 대해
3. "dot" syntax를 사용할 수 있도록 제공해준다.
✔️ 1. 만약 dynamicMemberLookup을 "사용"하는 타입이 있다면,
이를 위해 이 타입이 dynamicMemberLookup를 사용한다고 알려주어야 합니다.
이는 @dynamicMemberLookup attribute를 타입 앞에 붙이면 됩니다.
@dynamicMemberLookup ✅
struct Contact {
var persons: [String: String]
subscript(planet: String) -> String? {
return self.persons[planet]
}
}
이렇게 말이죠. (set 연산은 일단 지울게요)
자..그럼 에러가 날거에요.
왜냐하면 dynamicMemberLookup이 요구하는 메소드가 있기 때문입니다.
우리가 위에서 봤던 subscript메소드를 요구하는데.. dynamicMember라는 파라미터를 가진 메소드를 요구하네요.
수정해주겠습니다.
@dynamicMemberLookup
struct Contact {
var persons: [String: String]
subscript(dynamicMember planet: String) -> String? {
return self.persons[planet]
}
}
여기까지는 이해하셨죠?
자..그럼 빌드하고 아까 우리가 작성했던 코드들을 테스트해봅시다.
var contact = Contact(persons: [
"지구": "Zedd",
"달": "Marshmello"
])
let zedd = contact[dynamicMember: "지구"] // Zedd
똑같은데, dynamicMember라는 파라미터를 같이 쓰도록만 바뀌었습니다. 동작에는 아무 이상이 없군요.
✔️ 2. 이 타입은 런타임시 확인되는 임의의 "name"에 대해
✔️ 3. "dot" syntax를 사용할 수 있도록 제공해준다.
이거는 한꺼번에 보겠습니다.
var contact = Contact(persons: [
"지구": "Zedd",
"달": "Marshmello"
])
let zedd = contact[dynamicMember: "지구"] // Zedd
이렇게 dynamicMember subscript를 이용해서 값을 가져올 수도 있지만!!!!!!
var contact = Contact(persons: [
"지구": "Zedd",
"달": "Marshmello"
])
let zedd = contact.지구 // Zedd
이게 가능해진다는 것입니다.
만약 없는 값으로 부르면ㅇ
var contact = Contact(persons: [
"지구": "Zedd",
"달": "Marshmello"
])
let zedd = contact.화성 // nil
이렇게 nil이 나오게 됩니다.
🙋♀️ Q : 지구...? Contact에 지구라는 프로퍼티가 있었어?
👩💻 A :
@dynamicMemberLookup
struct Contact {
var persons: [String: String]
subscript(dynamicMember planet: String) -> String? {
return self.persons[planet]
}
}
네 없죠. 없는데,
이 dynamicMemberLookup이 subscript를 우리에게 친숙한 dot syntax로 호출할 수 있도록 도와주는 것이죠.
사실
contact[dynamicMember: "지구"] // Zedd
contact[dynamicMember: "화성"] // nil
contact.지구 // Zedd
contact.화성 // nil
위나 아래나 똑같습니다.
그래서 SE-0195 에서도 dynamicMemberLookup을 syntactic sugar로 설명합니다.
(This provides syntactic sugar that allows the user to write..)
🙋♀️ Q : 이게 어떻게 가능한지...
👩💻 A : 컴파일러가 subscript를 평가할 때, subscript가 "런타임"에 동적으로 호출되기 때문에 가능.
(왜 "dynamicMemberLookup"인지 아시겠나요!!)
🙋♀️ Q : subscript(dynamicMember: )에 String타입 말고 다른 타입을 넣고싶어!
👩💻 A : ExpressibleByStringLiteral (프로토콜) 또는 keyPath만 넣을 수 있습니다.
String은
ExpressibleByStringLiteral을 conform하고 있기에 들어갈 수 있는 것입니다.
ExpressibleByStringLiteral를 conform하고 있는 다른 타입들은
- AnimationFormatString.OSLogMessage
- FilePath
- LanguageTag
- NSString
- NWEndpoint.Host
- OSLogMessage
- PreviewDevice
- Selector
- StaticString
- String
- Substring
- Target.Dependency
- Version
이 있습니다. (참고 : ExpressibleByStringLiteral)
KeyPath를 넣는 방법이 있는데..이건 다른 글에서 공부하도록 할게요 ~.~
참고
docs.swift.org/swift-book/ReferenceManual/Attributes.html
'Swift' 카테고리의 다른 글
Introducing Swift Collections (0) | 2021.04.13 |
---|---|
dynamicCallable (0) | 2021.03.18 |
Property Wrapper (2) | 2021.03.07 |
Key-Value Observing(KVO) in Swift (2) | 2021.03.01 |
Key-Value Coding(KVC) / KeyPath in Swift (4) | 2021.02.28 |
- Xcode
- 스위프트
- Accessibility
- swift delegate
- SwiftUI
- swift array
- np-complete
- WWDC
- 피아노
- np-hard
- Swift
- github
- iOS delegate
- Git
- UIBezierPath
- 회고
- 스위프트 문법
- fastlane
- swift sort
- WidgetKit
- 제이슨 파싱
- FLUTTER
- ios 13
- swift tutorial
- actor
- swift3
- swift 공부
- WKWebView
- Combine
- IOS
- Total
- Today
- Yesterday