Published 2022. 10. 5. 21:43

KVC란, Key-Value-Coding을 의미하며,

캡슐화된 객체에서 gettersetter를 통한 직접 접근이 아닌

Key, KeyPath값을 통해 인스턴스의 value(property)간접적으로 접근할 수 있도록 NSKeyValueCoding protocol에 의해 제공되는 machanism

 

KVC에서 핵심인 Key와 KeyPath에 대해 알아보자

Key

우선 Key는 위에서 보았듯이, value(property)에 간접적으로 접근 할 수 있게 해준다.

key값은 property의 이름과 같아야하며, 

ASCII 코드의 String이다

 

하지만, Key의 경우에 Objective-C의 문법으로 Swift에서 사용할 경우 

Objective-C의 최상위 Root Class인 NSObject를 상속 받아야한다.

NSObject는 기본적으로 NSKeyValueCoding protocol을 채택하고 있다.

 

또한, 변수는 @objc 타입으로 선언하여야 한다.

class Address: NSObject {
    @objc var cityName: String?
    var roadName: String?

}

let address = Address()
address.value(forKey: "cityName") //nil
address.setValue("seoul", forKey: "cityName")
address.value(forKey: "cityName") //seoul
address.value(forKey: "roadName") //Error

 

@objc타입으로 선언하지 않은 변수에 경우에는 Key를 사용할 수 없다.

 

KeyPath

Key와 같이 문자열이지만, (.)으로 구분을 짓는방식이며, 

상위 객체를 기준으로 하위 객체의 property로 계층적으로 접근하는 방식이다.

 

\Type.property_name

<keyPath Syntax>

struct Address {
    var cityName: String
}

struct Person {
    var name: String
    var address: Address
}

let address = Address(cityName: "seoul")
let person = Person(name: "Jung", address: address)

person[keyPath: \.address.cityName] //seoul
//keyPath를 활용한 간접 접근 방식

person.address.cityName //seoul
//직접 접근 방식

위의 <KeyPath Syntax> 에서는 Type을 쓰라 하였지만, 

person 자체가 Person 타입이기에 생략이 가능하다. 

하지만 다른 곳에 사용하는 경우 Type을 명시 해주어야한다.

 

KeyPath에는 다음과 같은 종류가 있다. 

  • AnyKeyPath
  • PartialKeyPath<Root>
  • KeyPath<Root, Value> : only read
  • WritableKeyPath<Root, Value>
  • ReferenceWritableKeyPath<Root, Value>

 

WriteableKeyPath 

value type 인스턴스에 사용이 가능하며, 변경 가능한 모든 property에 대해 read write가 가능하다.

var address = Address(cityName: "seoul")
var person = Person(name: "Jung", address: address)

let writableKeyPath = \Person.address.cityName

/* 2개가 같은 표현임.
 let addresKeyPath = \Person.address
 let cityNameKeyPath = addresKeyPath.appending(path: \.cityName)
*/

person[keyPath: writableKeyPath] = "busan"
person[keyPath: writableKeyPath] //busan

이때 만약에 property가 let이거나 let으로 instance를 생성한 경우 keyPath로 자동으로 인식된다.

 

KeyPath

struct Address{
    var cityName: String 
}

struct Person{
    let name: String // let Property
    var address: Address

}

 

var address = Address(cityName: "seoul")
var person = Person(name: "Jung", address: address)

let keyPath = \Person.name
person[keyPath: keyPath]
person[keyPath: keyPath] = "ulsan" //Error

 

Person의 name property가 let으로 선언이 되었기 때문에, 

keyPath가 read만 가능한 KeyPath타입으로 된다.

 

let address2 = Address(cityName: "busan")
address2[keyPath: \.cityName] = "ulsan" //Error

여기서도 Error가 뜨는데 이는 Swift의 Struct는 

Value 타입이기 때문에 keyPath가 read만 가능한 KeyPath타입으로 된다.

 

 

ReferenceWritableKeyPath

Class 인스턴스에 사용이 가능하며, 변경 가능한 모든 property에 대해 read write가 가능하다.

class Address{
    var cityName: String
    
    init(cityName: String) {
        self.cityName = cityName
    }
}

class Person{
    let name: String  // let property
    var address: Address
    
    init(name: String, address: Address) {
        self.name = name
        self.address = address
    }
}
var address = Address(cityName: "seoul")
var person = Person(name: "Jung", address: address)

let referenceWritableKeyPath = \Person.address.cityName

person[keyPath: referenceWritableKeyPath]
person[keyPath: referenceWritableKeyPath] = "ulsan"

 

반면!

 

let keyPath = \Person.name

person[keyPath: keyPath] = "Kim" //Error

Person 의 name property가 let 이기 때문에, 

keypath가 KeyPath타입으로 된다!

 

let address2 = Address(cityName: "busan")

let referenceWritableKeyPath = \Address.cityName
address2[keyPath: referenceWritableKeyPath] = "ulsan"
address2[keyPath: referenceWritableKeyPath] // ulsan

위의 경우에는 Error가 나지 않는데, 

그 이유는 Swift의 class는 reference 타입이기 때문이다!

즉 인스턴스의 reference를 바꾸지 못하는 것일뿐 내부 property의 값은 (var 타입이라면!) 바꿀수 있다.

 

 

 

 

Reference

'iOS > Swift' 카테고리의 다른 글

[Swift] KVO, Delegate and Notification  (2) 2022.10.08
[Swift] Notification  (0) 2022.10.08
[Swift] Delegate  (0) 2022.10.07
[Swift] KVO (Key-Value-Observing)  (0) 2022.10.06
[Swift] Subscript  (4) 2022.10.05
복사했습니다!