IOS开发之旅-KVC【键值编码】

  在日常开发中,读取修改对象的属性值时,通常是点调用对应的属性进行相关操作。另外一种方式是通过键值编码,简称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运行时中使用元数据打开对象并进入其中查找需要的信息。
原文地址:https://www.cnblogs.com/PerfectSoft/p/4448909.html