티스토리 뷰
안녕하세요 :) Zedd입니다.
오늘은...!! dynamicCallable 공부!
dynamicCallable
🧑💻 Q : dynamicMemberLookup 이랑 비슷한 느낌...
🙋 A : 맞습니다..비슷한 친구입니다.
[특징]
✔️ SE-0216 에서 처음 소개. Swift 5.0에서 구현됨. (참고 Swift 5.0 변경사항)
(DynamicMemberLookup proposal - SE-0195 의 후속조치임)
✔️ 타입을 callable하게 만듬
# dynamicCallable
1. 만약 dynamicCallable을 "사용"하는 타입이 있다면,
2. 이 타입의 인스턴스를 호출할 수 있게 된다.
1. 만약 dynamicCallable을 "사용"하는 타입이 있다면,
바로 예제로 보겠습니다. dynamicMemberLookup에서 본 예제입니다.
struct Contact {
var persons: [String: String]
}
dynamicMemberLookup이 @dynamicMemberLookup attribute를 타입앞에 붙혔던 것 처럼,
dynamicCallable역시 @dynamicCallable attribute를 타입 앞에 붙히면 됩니다.
@dynamicCallable ✅
struct Contact {
var persons: [String: String]
}
이렇게요.
이러면 이제 이 타입은 "callable type"이 되게 됩니다.
이제 이런 오류가 나실텐데요,
dynamicMemberLookup이 subscript메소드를 요구했던것 처럼,
dynamicCallable도 요구하는 메소드가 있습니다.
func dynamicallyCall(withArguments args: <#Arguments#>) -> <#R1#>
func dynamicallyCall(withKeywordArguments args: <#KeywordArguments#>) -> <#R2#>
이렇게 2가지 중 1개를 구현해야만합니다.
return은 있어도 없어도 상관없습니다.
위 메소드들을 구현할 때 제약이 몇가지 있습니다만...지금 바로 보진 않을게요. 일단 예제를 보고 익숙해집시다!
그럼 저는 상단에 있는 친구를 구현해주겠습니다.
@dynamicCallable
struct Contact {
var persons: [String: String]
func dynamicallyCall(withArguments args: [String]) {
}
}
이렇게요.
필요한 메소드를 구현했으니 에러는 없어집니다.
2. 이 타입의 인스턴스를 호출할 수 있게 된다.
사실 이걸 어떻게 설명해야하나...고민을 했는데, 이해가 안가시더라도 예제로 보면 될 것 같습니다.
let contact = Contact(persons: [
"지구": "Zedd",
"달": "Marshmello"
])
contact() ✅ 가능
initializer가 아닙니다. contact라는 인스턴스를 "호출"할 수 있게 된거죠.
이 뿐만이 아닙니다.
contact()
contact("지구")
contact("지구", "달")
이런것도 가능해집니다.
왜냐면 파라미터 타입으로 [String]을 넣고있기 때문이죠.
func dynamicallyCall(withArguments args: [String]) {
}
[String]이 아니고 그냥 아무 타입이든 넣어도 됩니다.
func dynamicallyCall(withArguments args: [Int]) {
}
let contact = Contact(persons: [
"지구": "Zedd",
"달": "Marshmello"
])
contact()
contact(1)
contact(2, 3)
Int면 이런식으로 넣어야겠죠?
🧑💻 Q : 지금 dynamicallyCall이 [Int]을 요구하고 있는데, 왜
contact()
contact(1)
contact(2, 3)
이런식으로 넣어주는거야?
contact([])
contact([1])
contact([2, 3])
이게 맞지 않아?
🙋 A : 사실상
contact()
contact(1)
contact(2, 3)
이 코드는
contact.dynamicallyCall(withArguments: [])
contact.dynamicallyCall(withArguments: [1])
contact.dynamicallyCall(withArguments: [2, 3])
이 코드의 syntactic sugar이므로..사실상 배열화(?)가 된 상태라고 보면 될 것 같습니다.
위에서 보셨다시피
저렇게 인스턴스를 호출하게 되면
func dynamicallyCall(withArguments args: [AnyObject]) -> AnyObject
func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, AnyObject>) -> AnyObject
이 dynamicallyCall메소드가 불리게 되는데요, 우리가 아까 구현해준 dynamicallyCall에 로직을 추가해보겠습니다.
func dynamicallyCall(withArguments args: [String]) {
args.forEach {
print(self.persons[$0])
}
}
받은 args를 돌면서 person에 해당 arg key를 가진 value를 print합니다.
결과는 당연히
contact()
contact("지구") // prints Optional("Zedd")
contact("지구", "달") // prints Optional("Zedd") Optional("Marshmello")
이렇게 됩니다.
대충 어떤 느낌으로 사용하는지 아시겠나요?
# 메소드 제약사항
[1] func dynamicallyCall(withArguments args: <#Arguments#>) -> <#R1#>
[2] func dynamicallyCall(withKeywordArguments args: <#KeywordArguments#>) -> <#R2#>
그럼 위 메소드들의 제약사항을 보겠습니다.
[1]
func dynamicallyCall(withArguments args: <#Arguments#>) -> <#R1#>
withArguments에는 ExpressibleByArrayLiteral를 준수하는 타입만 들어갈 수 있습니다.
우리가 위에서 봤듯이 가장 만만한 Array가 들어갈 수 있겠죠.
ex.
func dynamicallyCall(withArguments args: [String]) -> 임의의 타입
func dynamicallyCall(withArguments args: [Int]) -> 임의의 타입
리턴타입에는 특별한 제약은 없습니다.
[2]
func dynamicallyCall(withKeywordArguments args: <#KeywordArguments#>) -> <#R2#>
withArguments ExpressibleByDictionaryLiteral를 준수하는 타입만 들어갈 수 있습니다.
가장 만만한 Dictionary가 들어갈 수 있습니다. 1번과 마찬가지로 리턴타입에는 특별한 제약이 없습니다.
ex.
func dynamicallyCall(withKeywordArguments args: [String: String]) -> 임의의 타입
또는 KeyValuePair가 들어갈 수 있습니다. (KeyValuePair가 ExpressibleByDictionaryLiteral를 준수하고 있으므로)
func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, String>) -> 임의의 타입
근데 [2] 메소드 같은 경우에는 Key의 타입이 무조건 String이어야 하더라구요..!?
[2] 메소드도 사용해봅시다.
func dynamicallyCall(withKeywordArguments args: [String: String]) {
args.forEach { print(persons[$0.value]) }
}
contact()
contact("지구") // prints Optional("Zedd")
비슷하게 사용하면 됩니다!
🙋 Q : persons[$0.key]가 아니라 print(persons[$0.value]인 이유?
🧑💻 A: 사실상
contact()
contact("지구")
이 코드는
contact.dynamicallyCall(withKeywordArguments: [:])
contact.dynamicallyCall(withKeywordArguments: ["": "지구"])
이 코드의 syntactic sugar입니다. 제가 준 값이 key가 아닌 value로 들어가게 됩니다.
Q : key가 왜 ""로 들어가?
A : 그냥 스펙입니다..!? .따로 key를 지정해주지 않으면 빈 문자열이 key로 들어가게 됩니다.
그래서 만약
@dynamicCallable
struct Contact {
var persons: [String: String]
func dynamicallyCall(withKeywordArguments args: [String: String]) {
args.forEach { print(persons[$0.value]) }
}
}
...
contact("지구", "달")
이런 코드를 넣었다면..런타임 에러가 발생하게 됩니다.
왜냐면
contact.dynamicallyCall(withKeywordArguments: ["": "지구", "": 달])
이렇게 desugar 되기 때문이죠. 그래서 duplicate keys에러가 발생하게 됩니다.
이 에러를 내고싶지 않다면
[1]
contact()
contact("지구") // prints Optional("Zedd")
contact(a: "지구", b: "달") // prints Optional("Zedd") Optional("Marshmello")
이렇게 key를 지정해주거나
[2]
@dynamicCallable
struct Contact {
var persons: [String: String]
func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, String>) {
args.forEach { print(persons[$0.value]) }
}
}
let contact = Contact(persons: [
"지구": "Zedd",
"달": "Marshmello"
])
contact()
contact("지구") // prints Optional("Zedd")
contact("지구", "달") // prints Optional("Zedd") Optional("Marshmello")
dynamicallyCall 메소드에 Dictionary대신 중복 key를 허용하는 KeyValuePairs타입을 넣으면 됩니다.
# 메소드 2개 다 구현해보기
[1] func dynamicallyCall(withArguments args: <#Arguments#>) -> <#R1#>
[2] func dynamicallyCall(withKeywordArguments args: <#KeywordArguments#>) -> <#R2#>
둘 중 하나의 메소드만 구현해도 컴파일에러가 사라졌었는데요,
둘 다 구현해보겠습니다.
@dynamicCallable
struct Contact {
var persons: [String: String]
func dynamicallyCall(withArguments args: [String]) {
args.forEach {
print("\(self.persons[$0]) Array")
}
}
func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, String>) {
args.forEach {
print("\(self.persons[$0.value]) KeyPair")
}
}
}
let contact = Contact(persons: [
"지구": "Zedd",
"달": "Marshmello"
])
contact()
contact("지구")
// Optional("Zedd") Array
contact("지구", "달")
// Optional("Zedd") Array
// Optional("Marshmello") Array
contact(a: "지구", b: "달")
// Optional("Zedd") KeyPair
// Optional("Marshmello") KeyPair
그냥 이렇다~~만 봐주세요 ㅎㅎ
참고
github.com/apple/swift-evolution/blob/master/proposals/0216-dynamic-callable.md
'Swift' 카테고리의 다른 글
Swift 5.4 Released! (3) | 2021.04.28 |
---|---|
Introducing Swift Collections (0) | 2021.04.13 |
dynamicMemberLookup (0) | 2021.03.11 |
Property Wrapper (2) | 2021.03.07 |
Key-Value Observing(KVO) in Swift (2) | 2021.03.01 |
- fastlane
- swift sort
- Swift
- UIBezierPath
- actor
- Combine
- Accessibility
- 스위프트 문법
- Git
- swift delegate
- 스위프트
- ios 13
- 회고
- 피아노
- github
- IOS
- Xcode
- WKWebView
- WidgetKit
- swift 공부
- np-complete
- swift3
- WWDC
- swift tutorial
- SwiftUI
- 제이슨 파싱
- swift array
- FLUTTER
- np-hard
- iOS delegate
- Total
- Today
- Yesterday