KVCとは
- Key Value Codingの略
- オブジェクトの属性値にたいしてプロパティや変数から直接アクセスするのではなく、間接的に文字列(key)を通してアクセスする手法
SwiftでKVCを行うには
-
NSObjectを継承する
- NSKeyValueCodingプロトコルが自動的に適合される
-
keyの指定は、#keyPath式が使える
-
サンプルコード
foo_kvc.swift
import Foundation
class Person: NSObject {
@objc let name: String
@objc let age: Int
@objc var income: Int
init(_ name: String, _ age: Int) {
self.name = name
self.age = age
self.income = 0
}
}
let p = Person("tanaka", 27)
// keyを通しての値の取得
print(p.value(forKey: #keyPath(Person.name)) as! String)
// letプロパティを変更するとクラッシュ
// p.setValue(30, forKey: #keyPath(Person.age)) クラッシュ
// varプロパティは変更出来る
p.setValue(350, forKey: #keyPath(Person.income))
NSKeyValuCodingプロトコル
以下のメソッドが用意されている。
-
keyを使った属性へのアクセス
- value(forKey:)
- setValue(_: forKey:)
-
keyPathを使った属性へのアクセス = 「.」を使って、オブジェクトの属性を横断することが出来る
- value(forKeyPath:)
- setValue(_: forKeyPath:)
-
それぞれ動作のカスタマイズが可能 (メソッドをオーバーライドする)
keyPathテクニック
- 配列の要素に対しても行える
配列の要素に対してKVCによる値の取得.swift
class Child: NSObject {
@objc var value: Int
init(_ value: Int) { self.value = value }
}
class Parent: NSObject {
@objc var children: [Child]
init(_ children: [Child]) { self.children = children }
}
let p = Parent(Array(1..<5).map(Child.init))
let values = p.value(forKeyPath: #keyPath(Parent.children.value)) as! NSArray // (or [Int] | [NSNumber]
- 配列演算子も用意されている
swift_kvc_collection_operator.swift
// KVCを使って平均値を求める
let avg = p.value(forKeyPath: "children.@avg.value") as! Float
NOTE:
- mapメソッドやfor文を使用した方が断然速い (5倍ぐらい差がありました)
KVCを使ってコードのシンプル化例
シンプル化前.m
- (id)tableView:(NSTableView *)tableview objectValueForTableColumn:(id)column row:(NSInteger)row
{
id result = nil;
Person *person = [self.people objectAtIndex:row];
if ([[column identifier] isEqualToString:@"name"]) {
result = [person name];
} else if ([[column identifier] isEqualToString:@"age"]) {
result = @([person age]); // Wrap age, a scalar, as an NSNumber
} else if ([[column identifier] isEqualToString:@"favoriteColor"]) {
result = [person favoriteColor];
} // And so on...
return result;
}
シンプル化.m
- (id)tableView:(NSTableView *)tableview objectValueForTableColumn:(id)column row:(NSInteger)row
{
return [[self.people objectAtIndex:row] valueForKey:[column identifier]];
}
KVCが使われている箇所
- Core Data
- Key Value Observer (KVO)
- Cocoa binding (iOSにはない)