4T2F / ThinkBig2

🌟씽크빅 2팀 스터디 🌟
2 stars 0 forks source link

Swift에서 키 경로(Key Path)란 무엇이며, 어떻게 사용하나요? #26

Open Phangg opened 7 months ago

Phangg commented 7 months ago

Swift에서 키 경로(Key Path)란 무엇이며, 어떻게 사용하나요?

Phangg commented 7 months ago

KeyPath

[ KeyPath 공식문서 ]

가장 기본적인 KeyPath 의 모양

KeyPath 형태

\.id 를 사용하듯.. 타입추론이 가능한 경우, BaseType 의 생략도 가능 & 프로퍼티 뒤에 추가적인 연결 가능

KeyPath 타입추론 & 연속된 프로퍼티

Optional 과 Subscript 의 사용도 가능

KeyPath 옵셔널 & 서브스크립트

KeyPath 서브스크립트 + 타입추론

KeyPath Type

KeyPath Type

코드 예시

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

KeyPath appending - 1

KeyPath appending - 1

KeyPath 의 Type

KeyPath 의 Type
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 제공

KeyPath 를 함수로 사용

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"]

Phangg commented 7 months ago

런타임에 키 경로를 사용하여 속성에 접근하는 방법과 KVO ( Key-Value Observing )

KVC ( Key-Value-Coding )

KVC 는 안정성이 떨어지고, 런타임에 오류를 가져올수도 있고, struct 와 enum 에서 사용이 어렵지만... KeyPath 대신, 런타임에 키 경로를 사용하여 속성에 접근할 수 있다는 것을 알 수 있음

KVO ( Key-Value Observing )

사용예시

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 - 변경 전, 후 상태 모두 파악시

KVO 는 분리된 파트간의 변경사항을 전달할 수 있고, 내부 소스 변경 없이 상태변화에 대응하고, 변경 전후의 값을 알 수 있는 장점이 있지만, NSObject 를 상속받아야하고, dealloc 시 옵저버를 지워줘야 하는 불편함이 있다.