プロパティーやインスタンス変数の値を文字列で指定して取得する (KVC) : Objective-C プログラミング
PROGRAM
プロパティーやインスタンス変数の値を文字列で指定して取得する (KVC)
KVC を使ってプロパティーを読み書きする
Objective-C では KVC という機能を使うことで、クラスに実装されているプロパティーの名前を文字列で指定して読み書きできるようになっています。
たとえば、プロパティーが "@property (readwrite,strong) NSString *name" で定義されているクラスがあったとします。
@interface EzObject : NSObject
@property (readwrite,strong) NSString *name;
@end
このとき、このクラスのインスタンス "object" に対して "-valueForKey:" メソッドと "-setValue:forKey:" メソッドを使って、次のように "name" プロパティーを読み書きできます。
// プロパティーの値を読み込む
NSString *value = [object valueForKey:@"name"];
// プロパティーに値 (value) を書き込む
[object setValue:value forKey:@"name"];
KVC を使ってインスタンス変数を読み書きする
セッターやゲッターが用意されていないインスタンス変数も KVC を使って読み書きできます。
たとえば、インスタンス変数 "NSString *_name" が定義されたクラスがあったとします。
@interface EzObject : NSObject
{
NSString *_name;
}
@end
このようなとき、このクラスのインスタンス "object" に対して、先ほど紹介した "name" プロパティーがあったときと全く同じ方法で、このインスタンス変数 "_name" を読み書きできます。
// インスタンス変数 "_name" の値を読み込む
NSString *value = [object valueForKey:@"name"];
// インスタンス変数 "_name" に値 (value) を書き込む
[object setValue:value forKey:@"name"];
ここで指定する名前は、インスタンス変数の名前が "_" で始まる場合は、それを取り除いた名前になります。インスタンス変数の名前が "_" で始まらない場合には、そのままの名前を指定することで、値を読み書きできます。
プロパティーやインスタンス変数がプリミティブ型の場合
プロパティーやインスタンス変数が数値型の場合
プロパティーやインスタンス変数のデータ型が NSInteger や int や double などといった数値型の場合は、それらは NSNumber 型として扱われます。
たとえば、次のように float 型のプロパティー "coefficient" があったとします。
@interface EzObject : NSObject
@property (readwrite) float coefficient;
@end
このようなとき、KVC では NSNumber 型を使って "-valueForKey:" メソッドと "-setValue:forKey:" メソッドを介して、プロパティーの値を読み書きすることになります。NSNumber 型とプリミティブ型との変換は自動的に行われます。
NSNumber *numberValue;
float floatValue = 10.2;
// プロパティー "coefficient" の値を読み込む
numberValue = [object valueForKey:@"coefficient"];
// プロパティー "coefficient" に値 (floatValue) を書き込む
[object setValue:@(floatValue) forKey:@"coefficient"];
プロパティーやインスタンス変数がポインター型の場合
プロパティーやインスタンス変数で扱うデータ型が char* などのプリミティブ型のポインターの場合、KVC でそれらの値を扱うことはできないようです。
そのような場合に "-setValue:forKey:" メソッドで値を書き込もうとすると、次のようなメッセージとともに NSUnknownKeyException 例外が発生します。
this class is not key value coding-compliant for the key $(PROPERTY_NAME)
ちなみに C 言語のポインターとは表記が似ていても、Objective-C の NSArray* 型などのいわゆる id 型の場合は、問題なく利用できます。
もし、配列を扱うためにプリミティブ型のポインターを使用している場合は、NSArray 型を使って値を扱うようにすると良いでしょう。このとき、プリミティブ型は NSArray で直接持つことはできないので、それぞれをさらに NSNumber 型にして持つ必要があります。
KVC で値に nil が指定されたときに既定値を設定する
KVC でプリミティブ型のプロパティーやインスタンス変数に nil を設定しようとすると、既定では次のようなメッセージとともに NSInvalidArgumentException 例外が発生します。
could not set nil as the value for the key $(PROPERTY_NAME)
プリミティブ型の値として nil が指定された場合に、既定値などの代わりの値を設定したい場合には、KVC で値を読み書きする対象のクラスに "-setNilValueForKey:" メソッドを実装して、そこで任意の値を設定するようにします。
- (void)setNilValueForKey:(NSString*)key
{
if ([key isEqualToString:@"coefficient"])
{
// 指定されたキーが "coefficient" の場合に既定値を設定します。
self.coefficient = 1.0;
}
else
{
// 代わりの値を想定していないキーは、親クラスの setNilValueForKey: に処理を委ねます。
[super setNilValueForKey:key];
}
}
NSString 型などの Objective-C オブジェクト型の場合には nil を問題なく設定できますが、そのときにはこの "-setNilValueForKey:" は呼び出されません。あくまでも KVC でプリミティブ型を対象に nil を設定しようとしたときにだけ呼び出されます。
[ もどる ]