키 경로 KeyPath
객체의 값을 바로 꺼내오는 것이 아닌, Key
또는 KeyPath
를 이용해서 간접적으로 프로퍼티 위치 참조나 데이터를 가져오거나 수정하는 방법이다.
키 경로를 사용해 간접적으로 특정 타입의 어떤 프로퍼티 값을 가리켜야 할지 미리 지정해 두고 사용 가능. 여기서 경로는 프로퍼티 이름이라고 생각하면 된다.
키 경로는 역슬래시( \
) 와 타입, 마침표( .
) 경로로 구성
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>
키 경로는 기존 키 경로에 하위 경로를 덧붙일 수도 있다.
let keyPath = \Stuff.owner
let nameKeyPath = keyPath.appending(path: \.name)
또, 각 인스턴스 keyPath
서브 스크립트 메서드에 키 경로를 전달해 프로퍼티에 접근도 가능하다.
struct Address {
var town: String
}
struct Person {
var address: Address
}
let address = Address(town: "한옥마을")
let Seogon = Person(address: address)
let SeogonAddress = Seogon[keyPath: \.address] //Address(town: "한옥마을")
let SeogonTown = Seogon[keyPath: \.address.town] //한옥마을
키 경로 만드는 법
class Person {
let name: String
init(name: String) {
self.name = name
}
}
struct Stuff {
var name: String
var owner: Person
}
let seogun = Person(name: "서근")
var iPhone = Stuff(name: "아이폰", owner: seogun)
let referenceWritableKeyPath = \Stuff.name
iPhone[keyPath: referenceWritableKeyPath] //아이폰
이런 식으로 키 경로를 만들어서 사용하고 싶은 곳에 사용 가능! 예를 들자면 아래와 같다
class Person {
let name: String
init(name: String) {
self.name = name
}
}
struct Stuff {
var name: String
var owner: Person
}
let seogun = Person(name: "서근")
let cheolsu = Person(name: "철수")
let iPhone = Stuff(name: "아이폰 12 mini", owner: seogun)
var Macbook = Stuff(name: "맥북프로", owner: cheolsu)
let airPod = Stuff(name: "에어팟 프로", owner: seogun)
let stuffNameKeyPath = \Stuff.name
let ownerKeyPath = \Stuff.owner
// ownerKeyPath의 owner은 class Person을 참조하고 있기때문에
// \Stuff.owner.name 과 같은 표현
let ownerNameKeyPath = ownerKeyPath.appending(path: \.name)
print(iPhone[keyPath: stuffNameKeyPath]) //아이폰 12 mini
print(Macbook[keyPath: stuffNameKeyPath]) //맥북 프로
print(airPod[keyPath: stuffNameKeyPath]) //에어팟 프로
print(iPhone[keyPath: ownerNameKeyPath]) //서근
print(Macbook[keyPath: ownerNameKeyPath]) //철수
print(airPod[keyPath: ownerNameKeyPath]) //서근
//키 경로와 서브스크립트를 이용해 프로퍼티에 접근해 값을 변경할 수 있음
//Macbook은 변수로 선언 했기 때문에 가능
Macbook[keyPath: stuffNameKeyPath] = "맥북 M1"
print(Macbook[keyPath: stuffNameKeyPath]) //맥북 M1
Macbook[keyPath: ownerKeyPath] = seogun
print(Macbook[keyPath: ownerNameKeyPath]) //서근
//반대로 상수로 선언한 타입과 읽기 전용 프로퍼티는 값을 바꿔줄 수 없음
iPhone[keyPath: stuffNameKeyPath] = "아이폰 13 pro"
//error : Cannot assign through subscript: 'iPhone' is a 'let' constant
seogun[keyPath: \Person.name] = "라쿤"
//error : 키 경로가 읽기 전용입니다.
KeyPath 종류
WritableKeyPath
struct Person {
var name: String
}
struct Stuff {
var owner: Person
}
let writableKeypath = \Stuff.owner.name
person
의 name
이 변수 이기 때문에 변경 가능한 모든 프로퍼티에 대한 read & write access를 제공하기 때문에 writableKeyPath
가 된다. 만약 name
이 상수이면 수정이 불가(Read-only
) 하므로 WritableKeyPath
가 아닌 일반적인 KeyPath
타입이 된다.
ReferenceWritableKeyPath
class Person {
var name: String
init(name: String) {
self.name = name
}
}
class Stuff {
var name: String
var owner: Person
init(name: String, owner: Person) {
self.name = name
self.owner = owner
}
}
let seogun = Person(name: "서근")
var iPhone = Stuff(name: "아이폰", owner: seogun)
let referenceWritableKeyPath = \Stuff.name
iPhone[keyPath: referenceWritableKeyPath] //아이폰
위에서 사용한 코드는 class
를 사용하고 있기 때문에 자동으로 ReferenceWritableKeyPath
타입이 됨.
읽어주셔서 감사합니다🤟
'SWIFT > Grammar' 카테고리의 다른 글
Swift : 기초문법 [메서드 #2 mutating] (0) | 2022.01.14 |
---|---|
Swift : 기초문법 [메서드 #1 인스턴스 메서드, self 프로퍼티] (0) | 2022.01.13 |
Swift : 기초문법 [프로퍼티#4 - 타입 프로퍼티] (0) | 2022.01.10 |
Swift : 기초문법 [프로퍼티#3 - 프로퍼티 옵저버(감시자) - didSet, willSet] (0) | 2022.01.09 |
Swift : 기초문법 [프로퍼티#2 - 연산 프로퍼티] (0) | 2022.01.08 |