Posted at

Swiftとキー値コーディング その2

More than 3 years have passed since last update.


辞書をキー値コーディングでアクセス

NSDictionary、NSMutableDictionaryはキー値コーディングをサポートしていて、valueForKeysetValueによる辞書の要素へのアクセスが可能です。

var dic : NSMutableDictionary = ["foo":1]

dic.setValue(2, forKey: "bar")
let n = dic.valueForKey("bar") as Int!

しかし、Swiftの辞書はキー値コーディングをサポートしません。

var dic1 = ["foo":1]

let n2 = dic1.valueForKey("foo") as Int! // エラー

キー値コーディングが必要な場合、Swiftの辞書は使用せず、NSDictionary、NSMutableDictionaryを使用する必要があります。


キーパスによる多段アクセス

Personクラスのインスタンスをメンバーに持つWorkingGroupを定義します。

class Person : NSObject

{
var name = ""
}

class WorkingGroup : NSObject
{
var leader : Person = Person()
}

このWorkingGroupのインスタンスから、Person型のプロパティであるleaderのnameプロパティにキー値コーディングでアクセスする場合、次のようになります。

// 通常のキー値コーデイングによるアクセス

var g1 = WorkingGroup()
var p1 = g1.valueForKey("leader") as Person!
p1.setValue("Jhon Doe", forKeyPath: "name") // g1.leader.nameに"Jhon Doe"をセット

しかしこれでは煩雑なので、キーパスと呼ばれるキー値を「.」で連結した値で入れ子になったオブジェクトにアクセスすることができるようになっています。例えば、leaderプロパティのnameプロパティにアクセスしたい場合、キーパス値は「leader.name」という風になります。

// キーパスによるアクセス

var g1 = WorkingGroup()
g1.setValue("Jhon Doe", forKeyPath: "leader.name")
let name = g1.valueForKeyPath("leader.name")) as String!

Swiftでもキーパスによるアクセスは問題なくサポートされているようです。


コレクションに対するアクセス

NSArrayやNSSetといったコレクションに対するキー値コーディングはその各々の要素に対するアクセスとなり結果もまたコレクションとなります。例えば、Personクラスの配列であるPersonsに対して、キー値「name」で値を取得すると、配列の要素である各Personオブジェクトのnameプロパティ値の配列が値として返ります。

class Person : NSObject

{
var name = ""
init(name: String)
{
self.name = name
}
}
var persons : NSMutableArray = [Person(name: "Kyusaku Yumeno"),
Person(name: "Mushitaro Oguri")]
// ["Kyusaku Yumeno","Mushitaro Oguri"]が返る
let names = persons.valueForKey("name") as NSArray

コレクションに対するsetVaueはコレクションの全要素に対するキー値に対応するプロパティへの値のセットとなります。

Personクラスの配列であるPersonsに対して、キー値「name」で値を設定すると、配列の要素であるすべてのPersonオブジェクトのnameプロパティに値を設定します。

var persons : NSMutableArray = [Person(name: "Kyusaku Yumeno"), 

Person(name: "Mushitaro Oguri")]
// personsの保持する二つのPersonオブジェクトのnameプロパティに値"Ranpo Edogawa"が設定される。
persons.setValue("Ranpo Edogawa", forKey: "name")

しかし辞書同様、Swiftの配列はキー値コーデイングをサポートしません。

var persons : [Person(name: "Kyusaku Yumeno"), 

Person(name: "Mushitaro Oguri")]
let names = persons.valueForKey("name") as [Person] // エラーになる!!

要するに、キー値コーディングの対象となるクラスが配列や辞書を保持する場合、Swiftネイティブな配列や辞書ではなく、Objective-C由来のNSArrayやNSDictionaryを利用しなければいけないということなのかな?