Open Phangg opened 7 months ago
\.self
를 많이 써왔을 텐데, 이게 가장 많이 봐왔던 KeyPath 이지 않을까경로
를 통해 접근하는 것\타입이름.프로퍼티이름
의 형태를 가지고 있으며, 컴파일을 거쳐 KeyPath 클래스의 인스턴스로 생성이 됨\.id
를 사용하듯.. 타입추론이 가능한 경우, BaseType 의 생략도 가능 & 프로퍼티 뒤에 추가적인 연결 가능struct Person {
var name: String
var age: Int
}
struct Study {
var leader: Person
var subject: String
var attending: [Person]
}
//
let manchae = Person(name: "manchae", age: 26)
let phang = Person(name: "phang", age: 30)
let hamfan = Person(name: "hamfan", age: 25)
let kmh = Person(name: "kmh", age: 28)
let xohxe = Person(name: "xohxe", age: 29)
//
var study02 = Study(leader: manchae, subject: "iOS", attending: [manchae, phang, hamfan, kmh, xohxe])
// Person 의 이름에 대한 KeyPath<Person, String>
let personNameKeyPath = \Person.name
// \Study.leader 의 타입추론 형식
let leader = study02[keyPath: \.leader]
print(leader) // Person(name: "manchae", age: 26)
// \Study.leader 의 타입추론 형식
study02[keyPath: \.leader] = xohxe
print(study02.leader) // Person(name: "xohxe", age: 29)
// Study 의 Leader 의 age 에 대한 KeyPath<Study, Int>
// study02 의 leader 의 age 에 만들어 둔 ketpath 로 접근
let studyLeaderAgeKeyPath = \Study.leader.age
let studyLeaderAge = study02[keyPath: studyLeaderAgeKeyPath]
print(studyLeaderAge) // 29
// KeyPath 를 타입으로 받는 메서드 사용 예시
func getStudyMemberAge(study: Study, path: KeyPath<Study, Person?>) -> Int {
let personAgePath = path.appending(path: \.?.age)
guard let age = study[keyPath: personAgePath] else { fatalError("Error!!") }
return age
}
print(getStudyMemberAge(study: study02, path: \Study.attending.first)) // 26
.appending
으로 path 를 추가하던데.. 이건 뭐지?Type | Description |
---|---|
AnyKeyPath | 타입이 지워진 KeyPath |
PartialKeyPath | 부분적으로 타입이 지워진 KeyPath |
KeyPath | Read-only get하는 용도로만 사용가능 |
WriteableKeyPath | Value type instance에 사용가능 변경 가능한 모든 Property (var) 에 대해 read & write access 제공 |
ReferenceWriteableKeyPath | Reference type instance에 사용가능 변경 가능한 모든 Property (var) 에 대해 read & write access 제공 |
//
let manchae = Person(name: "manchae", age: 26)
let phang = Person(name: "phang", age: 30)
let hamfan = Person(name: "hamfan", age: 25)
let kmh = Person(name: "kmh", age: 28)
let xohxe = Person(name: "xohxe", age: 29)
let studyMembers = [manchae, phang, hamfan, kmh, xohxe]
// 기존 후행 클로저 사용 let memberNames = studyMembers.map { $0.name } print(memberNames) // ["manchae", "phang", "hamfan", "kmh", "xohxe"]
// KeyPath 함수로 사용 let membersName = studyMembers.map(.name) print(membersName) // ["manchae", "phang", "hamfan", "kmh", "xohxe"]
우선, KeyPath 에 대해서 다시 얘기해보면 컴파일 시점에 결정이 되기 때문에 실행 시점에 속성 이름을 변경할 수 없다는 단점이 있다.
그렇기에 우리는 런타임에 동적으로 키 경로를 사용하는 방법을 궁금해하는 것..
KeyPath 의 처음 설명에서 Swift 4 부터 사용이 되었다고 했는데 그렇다면 그 전에는..?
이전에는 KVC ( Key-Value-Coding ) 가 사용이 되었다.
@objc
, dynamic
키워드를 추가해야 함 )setValue(_:forKey:)
와 value(forKey:)
메서드를 사용하여 값을 설정하거나 가져올 수 있음KVC 는 안정성이 떨어지고, 런타임에 오류를 가져올수도 있고, struct 와 enum 에서 사용이 어렵지만... KeyPath 대신, 런타임에 키 경로를 사용하여 속성에 접근할 수 있다는 것을 알 수 있음
observe
하려는 프로퍼티에 @objc
와 dynamic
을 추가해야 함 ( KVC..? )import Foundation
// class 사용, NSObject 상속
class Person: NSObject {
// @objc Attribute 사용 + dynamic Modifire 사용
@objc dynamic var name: String
init(name: String) {
self.name = name
}
}
var person = Person(name: "Phang")
// Observer
// old, new - 변경 전, 변경 후
var person1 = Person(name: "Phang")
person1.observe(\.name, options: [.old, .new]) { (object, change) in
print("Name changed from \(change.oldValue) to \(change.newValue)")
}
person1.name = "팽" // Name changed from Optional("Phang") to Optional("팽")
// initial - Observer 등록 전
var person2 = Person(name: "Kim")
person2.observe(\.name, options: [.old, .new, .initial]) { (object, change) in
print("Name changed from \(change.oldValue) to \(change.newValue)")
}
// Name changed from nil to Optional("Kim")
// prior - 변경 전, 후
var person3 = Person(name: "Hwang")
person3.observe(\.name, options: [.old, .new, .prior]) { (object, change) in
print("Name changed from \(change.oldValue) to \(change.newValue)")
}
person3.name = "Choi"
// Name changed from Optional("Hwang") to nil
// Name changed from Optional("Hwang") to Optional("Choi")
위 코드의 options 에 대한 설명
old - 변경 전 값 new - 변경 후 값 initial - Observer 등록 전 handler 호출 시 (newValue로 들어감) prior - 변경 전, 후 상태 모두 파악시
Swift에서 키 경로(Key Path)란 무엇이며, 어떻게 사용하나요?