Swift는 함수가 일급시민으로서 상수나 변수에 참조를 할당할 수 있다
Swift의 함수, 클로저에 대해 공부해 본 사람이라면 누구나 이 문장을 본 적이 있을 겁니다!
func someFunction(paramA: Any, paramB: Any) {
print("someFunction called...")
}
var functionReference = someFunction(paramA: paramB:)
functionReference("A", "B") //someFunction called...
functionReference = anotherFunction(paramA: paramB:)
그리고 이 예제를 보면서 공부하셨겠죠??ㅎ
며칠 전에 프로젝트를 진행하면서, "그럼 프로퍼티의 위치만 참조하는 방법은 없을까?"를 생각하게 되었는데.. 해당 프로퍼티의 타입이 값 타입이여서 어떻게 할 도리가 없었습니다...ㅠ
그러던 중! 키 경로
에 대해서 알게 되었죠ㅎㅎ
그럼 한 번 공부하러 가보죠
앞서 말했다 싶이 프로퍼티도 함수 처럼 값을 바로 꺼내오는 것이 아니라 어떤 프로퍼티의 위치만 참조
하도록 할 수 있습니다. 바로 키 경로
를 활용하는 것이지요
키 경로
타입은 AnyKeyPath
라는 클래스로부터 파생됩니다
공식 문서를 보면 any 루트 타입으로 부터 값 타입에 이르는 type-erased key path..? 라고 되어 있다..?? 잘 모르겠네...ㅠㅠ
일단, 다른 자료를 찾아보면 이 타입은
WritableKeyPath<Root, Value>, ReferenceWritableKeyPath<Root, Value> 타입으로 제일 많이 확장된 키 경로 타입이라고 한다
해당 타입들은 제네릭 클래스로
이러한 기능을 가지고 있다
근데 이것만 보면, 정확히 어떤 느낌인지 안 다가 올 것이다
일단 키 경로의 구성을 보자!!
\타입이름.경로.경로.경로
아하! 타입이름과 프로퍼티 이름들로 타고 들어가는 것이구만?!
class Person {
var name: String
init(name: String) {
self.name = name
}
}
struct Stuff {
var name: String
var owner: Person
}
print(type(of: \Person.name)) // ReferenceWritableKeyPath<Person, String>
print(type(of: \Stuff.name)) // WritableKeyPath<Stuff, String>
또 다른 예제
class Person {
let name: String
init(name: String) {
self.name = name
}
}
struct Stuff {
var name: String
var owner: Person
}
let yagom = Person(name: "yagom")
let hana = Person(name: "hana")
let macbook = Stuff(name: "Macbook Pro", owner: yagom)
var iMac = Stuff(name: "iMac", owner: yagom)
let iPhone = Stuff(name: "iPhone", owner: hana)
let stuffNameKeyPath = \Stuff.name
let ownerkeyPath = \Stuff.owner
let ownerNameKeyPath = ownerkeyPath.appending(path: \.name)
print(macbook[keyPath: stuffNameKeyPath]) // Macbook pro
print(iMac[keyPath: stuffNameKeyPath]) // iMac
키 경로를 잘 활용하면 프로토콜과 마찬가지로 타입 간의 의존성을 낮추는데 많은 도움을 준다!!
또, 애플의 프레임워크는 키-값 코딩 등 많은 곳에서 키 경로를 활용하므로, 알아두면 정말 좋다!!
그리고 고차 함수에서도 활용하는 방법이 있다!!
struct Person {
let name: String
let nickname: String?
let age: Int
var isAdult: Bool {
return age > 18
}
}
let yagom: Person = Person(name: "yagom", nickname: "bear", age: 100)
let hana: Person = Person(name: "hana", nickname: "na", age: 100)
let happy: Person = Person(name: "happy", nickname: nil, age: 3)
let family: [Person] = [yagom, hana, happy]
let names: [String] = family.map( \.name ) // family.map{ $0.name }
print(names)
잘 사용한다면 정말 여러 방면에서 사용할 수 있을 것 같다!!!
기억 안날 때마다 보자!!!