setValue()は配列で使用できないのでしょうか

投稿者: Anonymous
class Testdata: NSObject {
    dynamic var data1 = Data1()

    var updateDate: NSDate = NSDate()

    class Data1: NSObject {
        dynamic private(set) var version: [NSNumber] = []
        // unsignedChar配列として初期化しておく
    }
}

上記のような場合、以下のようにforKeyPathに配列を渡すことはできませんでした。
setValue(value: AnyObject?, forKeyPath: String)
配列に対しては使用できないのでしょうか。

let testdataobj = Testdata()
testdataobj.setValue(0x20, forKeyPath: "data1.version[0]")

解決

結論だけから言うとsetValue(_:forKeyPath:)valueForKeyPath(_:)で配列の要素を参照したり変更したりはできません。

他言語の似たような仕組みを知っていると「あれっ?」と思うのですが、設計思想が違うんで仕方ないといったところでしょうか。

class Testdata: NSObject {
    dynamic var data1 = Data1()

    class Data1: NSObject {
        //元の配列に十分なサイズがないとアクセスできません
        dynamic private(set) var version: [NSNumber] = [2,4,6]
    }
}
let testdataobj = Testdata()
//他言語なら当然サポートしてくれていそうなこの構文はサポートしていない
//testdataobj.setValue(0x20, forKeyPath: "data1.version[0]") //->クラッシュ

KVCを使って配列の要素を変更するときは、こんなメソッドを使う必要があります。

testdataobj.mutableArrayValueForKeyPath("data1.version")[0] = 0x20
print(testdataobj.data1.version[0]) //->32

mutableArrayValueForKeyPath(_:)の戻り値の型はNSMutableArrayなのですが、その実体は

print(testdataobj.mutableArrayValueForKeyPath("data1.version").dynamicType)
//->NSKeyValueSlowMutableArray

てな感じの内部クラスなので、別スレッドであったような更新時間の監視目的でオーバライドするのは難しいでしょう。

なかなか気難しいKVCなのですが、一方でコレクション演算子なんてものがあり、こんなことができたりします。

testdataobj.valueForKeyPath("[email protected]") //32+4+6
testdataobj.valueForKeyPath("[email protected]") //3

このあたり前にあげたKVCの公式ドキュメントをよく読めば…と書きたいところですが、私自身はよく読んでもわからずシンプルなコード例を作るのにさんざんPlayground上で実行時エラーを出してしまいました。
 Core Dataなどを活用する場合にも必要になってきますので、オンラインの他のリソースを合わせて参照しながら必要に応じて試しながら調べてみてください。

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *