IOS開発の旅-KVC【キー値符号化】

16937 ワード

日常開発では,修正オブジェクトの属性値を読み取る場合,通常は点呼び出しに対応する属性が関連操作を行う.もう1つの態様は、キー値符号化によりKVCと略称され、キー値符号化において主に以下の方法が用いられる
   /* Given a key that identifies an attribute or to-one relationship, return the attribute value or the related object. Given a key that identifies a to-many relationship, return an immutable array or an immutable set that contains all of the related objects.*/
- (id)valueForKey:(NSString *)key;


/* Given a value and a key that identifies an attribute, set the value of the attribute. Given an object and a key that identifies a to-one relationship, relate the object to the receiver, unrelating the previously related object if there was one. Given a collection object and a key that identifies a to-many relationship, relate the objects contained in the collection to the receiver, unrelating previously related objects if there were any.*/
- (void)setValue:(id)value forKey:(NSString *)key;


/* Key-path-taking variants of like-named methods. The default implementation of each parses the key path enough to determine whether or not it has more than one component (key path components are separated by periods). If so, -valueForKey: is invoked with the first key path component as the argument, and the method being invoked is invoked recursively on the result, with the remainder of the key path passed as an argument. If not, the like-named non-key-path-taking method is invoked.*/
- (id)valueForKeyPath:(NSString *)keyPath;



- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;


/* Given that an invocation of -valueForKey: would be unable to get a keyed value using its default access mechanism, return the keyed value using some other mechanism. The default implementation of this method raises an NSUndefinedKeyException. You can override it to handle properties that are dynamically defined at run-time.*/
- (id)valueForUndefinedKey:(NSString *)key;




/* Given that an invocation of -setValue:forKey: would be unable to set the keyed value using its default mechanism, set the keyed value using some other mechanism. The default implementation of this method raises an NSUndefinedKeyException. You can override it to handle properties that are dynamically defined at run-time*/
- (void)setValue:(id)value forUndefinedKey:(NSString *)key;


/* Given that an invocation of -setValue:forKey: would be unable to set the keyed value because the type of the parameter of the corresponding accessor method is an NSNumber scalar type or NSValue structure type but the value is nil, set the keyed value using some other mechanism. The default implementation of this method raises an NSInvalidArgumentException. You can override it to map nil values to something meaningful in the context of your application.*/
- (void)setNilValueForKey:(NSString *)key;




/* Given a dictionary containing keyed attribute values, to-one-related objects, and/or collections of to-many-related objects, set the keyed values. Dictionary entries whose values are NSNull result in -setValue:nil forKey:key messages being sent to the receiver.*/
- (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues;




/* Given an array of keys, return a dictionary containing the keyed attribute values, to-one-related objects, and/or collections of to-many-related objects. Entries for which -valueForKey: returns nil have NSNull as their value in the returned dictionary.*/
- (NSDictionary *)dictionaryWithValuesForKeys:(NSArray *)keys;

次に、KVC、Personを例示する.h:
#import <Foundation/Foundation.h>



@interface Person : NSObject



-(id) initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName age:(int)age;



@property (nonatomic,copy) NSString *firstName;

@property (nonatomic,copy) NSString *lastName;

@property (nonatomic,strong) Person *father;

@property int age;





@end

Person.m:
#import "Person.h"



@implementation Person



-(id)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName age:(int)age

{

    if(self = [super init])

    {

        self.firstName  = firstName;

        self.lastName   = lastName;

        self.age        = age;

    }

    return self;

}



//invoked by (setValue:forKey:) method,when it's given a nil value for a scalar value (such as int or float)

-(void)setNilValueForKey:(NSString *)key

{

    if([key isEqualToString:@"age"])

    {

        [self setValue:@18 forKey:@"age"];

    }

    else

    {

        [super setNilValueForKey:key];

    }

}



//invoked by (setValue:forKey:) method,when it finds no property corresponding to a given value

-(void)setValue:(id)value forUndefinedKey:(NSString *)key

{

    NSLog(@"%@ property not found",key);

}





//invoked by (valueForKey:) method,when it finds no property corresponding to a given value

-(id)valueForUndefinedKey:(NSString *)key

{

    NSLog(@"%@ property not found",key);

    return nil;

}



-(NSString *) description

{

    NSString *desc = [[NSString alloc] initWithFormat:@"firstName: %@, lastName:%@, age: %i", self.firstName,self.lastName,self.age];

    return desc;

}



@end

テストコード:
#import <Foundation/Foundation.h>

#import "Person.h"



int main(int argc, const char * argv[]) {

    @autoreleasepool {

        Person *person = [[Person alloc]initWithFirstName:@"xiao" lastName:@"long" age:26];

        NSLog(@"%@",person);

        //firstName: xiao, lastName:long, age: 26



        NSLog(@"%@",[person valueForKey:@"firstName"]);

        // xiao

        

        [person setValue:@"quan" forKey:@"lastName"];

        NSLog(@"%@",person.lastName);

        //quan

        

        

        //middlenName  , Person 【-(void)setValue:(id)value forUndefinedKey:(NSString *)key】

        [person setValue:@"pstune" forKey:@"middleName"];

        //middleName property not found

        

        //middlenName  , Person 【-(id)valueForUndefinedKey:(NSString *)key】

        [person valueForUndefinedKey:@"middleName"];

        //middleName property not found

        

        //age  , nil, Person 【-(void)setNilValueForKey:(NSString *)key】

        [person setValue:nil forKey:@"age"];

        NSLog(@"%@",person);

        //firstName: xiao, lastName:quan, age: 18

        

        

        Person *father = [[Person alloc]initWithFirstName:@"father" lastName:@"dad" age:50];

        person.father = father;

        

        NSLog(@"father firstName: %@",[person valueForKeyPath:@"father.firstName"]);

        //father firstName: father

        

        [person setValue:@"quan" forKeyPath:@"father.lastName"];

        NSLog(@"%@",father);

        //firstName: father, lastName:quan, age: 50

        

        NSDictionary *propertyDic = [person dictionaryWithValuesForKeys:@[@"firstName",@"lastName"]];

        NSLog(@"%@",propertyDic);

        /*

         {

         firstName = xiao;

         lastName = quan;

         }

         */

        

        [person setValuesForKeysWithDictionary:@{@"firstName":@"pstune",@"lastName":@"web"}];

        NSLog(@"%@",person);

        //firstName: pstune, lastName:web, age: 18



    }

    return 0;

}

KVCまとめ:
  • KVCの場合、Cocoaは自動的に箱詰めと開梱スカラー値(int,float,double)
  • を行う.
  • -(id)valueForKey:(NSString*)keyは、まずパラメータ名のgetterメソッドを検索します.このようなメソッドが存在しない場合は、オブジェクト内で名前のフォーマットを検索し続けます.keyまたはkeyのインスタンス変数
  • -(id)valueForKey:(NSString*)key Objective-C実行時にメタデータを使用してオブジェクトを開き、必要な情報を検索します.