Key-Value Coding (KVC)
Key-Value Coding은 객체의 프로퍼티를 Key-value 쌍으로 접근할 수 있도록 해주는 Objective-C 문법입니다.
KVC를 사용하면 속성의 이름을 문자열로 참조하여 런타임에 동적으로 객체의 프로퍼티 값을 설정하거나 읽을 수 있습니다.
Swift에서도 사용할 수 있지만, Objective-C 런타임에 의존하기 때문에 프로퍼티 선언 시 앞에 @objc 어노테이션을 붙여줘야 하며,
NSObject의 서브클래스에서만 사용이 가능합니다.
class Person: NSObject { // NSObject 서브클래스
@objc var name: String? // @objc 어노테이션
}
위와 같이 선언된 클래스의 프로퍼티는 아래처럼 KVC로 접근이 가능합니다.
let person = Person()
person.value(forKey: "name") // nil
person.name = "Cody"
person.value(forKey: "name") // Cody
만약 없는 프로퍼티명으로 접근하려 하면 런타임 에러가 발생하므로 주의해야 합니다.
person.value(forKey: "age") // runtime error(error: Execution was interrupted, reason: signal SIGABRT.)
KeyPath
KeyPath는 값에 대한 참조가 아닌, 프로퍼티에 대한 참조를 나타내는 타입입니다.
위의 Person의 name 프로퍼티를 참조하는 예시입니다.
let personNameKeyPath = \Person.name
print(personNameKeyPath) // Swift.ReferenceWritableKeyPath<__lldb_expr_47.Person, Swift.Optional<Swift.String>>
person인스턴스에서 keyPath를 통해 값을 받아오는 방법입니다.
subscript(keyPath:)를 통해서 값에 접근합니다.
let personName = person[keyPath: personNameKeyPath]
print(personName) // Optional("Cody")
Person 클래스에 친구 정보를 넣도록 수정하겠습니다.
class Person: NSObject {
@objc var name: String
@objc var friend1: Person?
@objc var friend2: Person?
init(name: String) {
self.name = name
}
}
그리고 Cody라는 name의 Person인스턴스를 만들고, Charlie와 Choi라는 친구를 넣어보겠습니다.
let charlie = Person(name: "Charlie")
let choi = Person(name: "Choi")
let cody = Person(name: "Cody")
cody.friend1 = charlie
cody.friend2 = choi
함수를 통해 keyPath를 사용하지 않고 cody의 friend1, friend2의 정보를 가져오려면 아래와 같이 작성해야 합니다.
func getFriend1(person: Person) -> Person? {
return person.friend1
}
func getFriend2(person: Person) -> Person? {
return person.friend2
}
getFriend1(person: cody)?.name // Charlie
getFriend2(person: cody)?.name // Choi
keyPath를 사용하면 위 두 함수를 하나로 작성할 수 있습니다.
func getFriend(person: Person, keyPath: KeyPath<Person, Person?>) -> Person? {
return person[keyPath: keyPath]
}
getFriend(person: cody, keyPath: \Person.friend1)?.name // Charie
getFriend(person: cody, keyPath: \.friend2)?.name // Choi
위에 예시처럼
Person의 friend1의 keyPath는 \Person.friend1으로 쓸 수도 있고,
getFriend()함수 파라메터 정의에서 KeyPath에 Person타입을 받는다고 명시를 했기 때문에 Person을 생략하고 \.friend1 으로 쓸 수도 있습니다.
(Swift5.2 이상) 컬렉션타입에서 고차함수에서 keyPath를 활용할 수 있습니다.
let persons = [cody, charlie, choi]
// 키패스를 사용하지 않고
let names = persons.map { $0.name } // ["Cody", "Charlie", "Choi"]
// 키패스를 사용해서
let namesUsingKeyPath = persons.map(\.name) // ["Cody", "Charlie", "Choi"]
KVC를 위해 @objc 어노테이션을 붙인 프로퍼티에 추가로 dynamic 어노테이션을 붙여주면 KVO(Key-Value Observing)을 사용할 수 있습니다. (KVO는 이전글 링크를 참조)
관련글
[Swift] Key-Value Observing(KVO)
Key-Value Observing(KVO)은 다른 객체의 property 변경에 대해 객체에 알리는 데 사용하는 코코아 프로그래밍 패턴입니다. Model과 View 사이와 같이 앱의 논리적으로 분리된 것 사이의 변경 사항을 전달하
swifty-cody.tistory.com
Key-Value Coding (KVC)
Key-Value Coding은 객체의 프로퍼티를 Key-value 쌍으로 접근할 수 있도록 해주는 Objective-C 문법입니다.
KVC를 사용하면 속성의 이름을 문자열로 참조하여 런타임에 동적으로 객체의 프로퍼티 값을 설정하거나 읽을 수 있습니다.
Swift에서도 사용할 수 있지만, Objective-C 런타임에 의존하기 때문에 프로퍼티 선언 시 앞에 @objc 어노테이션을 붙여줘야 하며,
NSObject의 서브클래스에서만 사용이 가능합니다.
class Person: NSObject { // NSObject 서브클래스
@objc var name: String? // @objc 어노테이션
}
위와 같이 선언된 클래스의 프로퍼티는 아래처럼 KVC로 접근이 가능합니다.
let person = Person()
person.value(forKey: "name") // nil
person.name = "Cody"
person.value(forKey: "name") // Cody
만약 없는 프로퍼티명으로 접근하려 하면 런타임 에러가 발생하므로 주의해야 합니다.
person.value(forKey: "age") // runtime error(error: Execution was interrupted, reason: signal SIGABRT.)
KeyPath
KeyPath는 값에 대한 참조가 아닌, 프로퍼티에 대한 참조를 나타내는 타입입니다.
위의 Person의 name 프로퍼티를 참조하는 예시입니다.
let personNameKeyPath = \Person.name
print(personNameKeyPath) // Swift.ReferenceWritableKeyPath<__lldb_expr_47.Person, Swift.Optional<Swift.String>>
person인스턴스에서 keyPath를 통해 값을 받아오는 방법입니다.
subscript(keyPath:)를 통해서 값에 접근합니다.
let personName = person[keyPath: personNameKeyPath]
print(personName) // Optional("Cody")
Person 클래스에 친구 정보를 넣도록 수정하겠습니다.
class Person: NSObject {
@objc var name: String
@objc var friend1: Person?
@objc var friend2: Person?
init(name: String) {
self.name = name
}
}
그리고 Cody라는 name의 Person인스턴스를 만들고, Charlie와 Choi라는 친구를 넣어보겠습니다.
let charlie = Person(name: "Charlie")
let choi = Person(name: "Choi")
let cody = Person(name: "Cody")
cody.friend1 = charlie
cody.friend2 = choi
함수를 통해 keyPath를 사용하지 않고 cody의 friend1, friend2의 정보를 가져오려면 아래와 같이 작성해야 합니다.
func getFriend1(person: Person) -> Person? {
return person.friend1
}
func getFriend2(person: Person) -> Person? {
return person.friend2
}
getFriend1(person: cody)?.name // Charlie
getFriend2(person: cody)?.name // Choi
keyPath를 사용하면 위 두 함수를 하나로 작성할 수 있습니다.
func getFriend(person: Person, keyPath: KeyPath<Person, Person?>) -> Person? {
return person[keyPath: keyPath]
}
getFriend(person: cody, keyPath: \Person.friend1)?.name // Charie
getFriend(person: cody, keyPath: \.friend2)?.name // Choi
위에 예시처럼
Person의 friend1의 keyPath는 \Person.friend1으로 쓸 수도 있고,
getFriend()함수 파라메터 정의에서 KeyPath에 Person타입을 받는다고 명시를 했기 때문에 Person을 생략하고 \.friend1 으로 쓸 수도 있습니다.
(Swift5.2 이상) 컬렉션타입에서 고차함수에서 keyPath를 활용할 수 있습니다.
let persons = [cody, charlie, choi]
// 키패스를 사용하지 않고
let names = persons.map { $0.name } // ["Cody", "Charlie", "Choi"]
// 키패스를 사용해서
let namesUsingKeyPath = persons.map(\.name) // ["Cody", "Charlie", "Choi"]
KVC를 위해 @objc 어노테이션을 붙인 프로퍼티에 추가로 dynamic 어노테이션을 붙여주면 KVO(Key-Value Observing)을 사용할 수 있습니다. (KVO는 이전글 링크를 참조)
관련글
[Swift] Key-Value Observing(KVO)
Key-Value Observing(KVO)은 다른 객체의 property 변경에 대해 객체에 알리는 데 사용하는 코코아 프로그래밍 패턴입니다. Model과 View 사이와 같이 앱의 논리적으로 분리된 것 사이의 변경 사항을 전달하
swifty-cody.tistory.com