IOS学习笔记之KVC、KVO

1、KVC的概念

    OC支持一种更灵活的操作方式,这种方式允许以字符串形式间接操作对象的属性,这种方式的全称是Key Value Coding(KVC),即键值编码

   KVC由NSKeyValueCoding提供支持,最基本的操作属性的两个方法如下:

  -setValue:forKey:  为指定的属性设置值

  -valueForKey:  获取指定属性的值

  无论调用setValue:forKey方法,还是调用valueForKey:方法,都是通过NSString对象来指定操作的属性值,其中forKey参数值用于传入属性名

       @interface StudentInformation : NSObject

       @property (nonatomic,copy) NSString *stuName;

       @property (nonatomic,assign) int stuAge;

       @property (nonatomic,copy) NSString *stuProfession;

       @property (nonatomic,assign) BOOL stuSex;

       @end


       StudentInformation *stu = [[StudentInformation alloc] init];

        //设置属性值

        [stu setValue:@"张三" forKey:@"stuName"];

        // @18  相当于 [NSNumber numberWithInt:18];

        [stu setValue:@18 forKey:@"stuAge"];

        [stu setValue:@"数学" forKey:@"stuProfession"];

        [stu setValue:@YES forKey:@"stuSex"];

        //获取属性值

        NSLog(@"name = %@,age = %@,profession = %@,sex = %@",[stu valueForKey:@"stuName"],[stu valueForKey:@"stuAge"],[stu valueForKey:@"stuProfession"],[[stu valueForKey:@"stuSex"] boolValue]?@"":@"");

       //- (id)valueForKey:(NSString *)key得到的是一个id类型的数据

        // int age = [[stu valueForKey:@"stuAge"] intValue];

        // NSNumber *number = [stu valueForKey:@"stuAge"]; int age = [number intValue];


2、setValue:forKey:方法的执行机制

  (1)程序优先调用"setUserName:属性值"代码通过setter方法设置值

  (2)如果该类没有setUserName:方法,那么KVC机制会搜索该类中名为_userName的成员变量,无论该成员变量是在类接口部分定义,还是在类实现部分定义,也无论用哪个访问控制符修饰。这条KVC代码底层实际上就是对_userName成员变量赋值

  (3)如果该类既没有setUserName:方法,也没有定义_userName成员变量,那么KVC机制会搜索该类中名为userName的成员变量,无论该成员变量是在类接口部分定义,还是在类实现部分定义,也无论用哪个访问控制符修饰。这条KVC代码底层实际上就是对userName成员变量赋值

  (4)如果上面3步都没有找到,那么系统将会执行该对象的setValue:forUndefinedKey:方法,实际上这个方法就是抛出一个异常,这个异常将会导致程序因为异常结束

3、valueForKey:方法执行机制

  (1)程序优先考虑调用"userName"代码通过getter方法获取值

  (2)如果该类没有userName方法,那么KVC机制会搜索该类中名为_userName的成员变量,无论该成员变量是在类接口中部分定义,还是在类实现部分定义,也无论用哪个访问控制符修饰。这条KVC代码底层实际上就是返回_userName成员变量的值

  (3)如果该类中既没有userName方法,也没有定义_userName成员变量,那么KVC机制会搜索该类中名为userName的成员变量,无论该成员变量是在类接口部分定义,还是在类实现部分定义,也无论用哪个访问控制符修饰。这条KVC代码底层实际上就是返回userName成员变量的值

  (4)如果上面3步都没有找到,那么系统将会执行该对象的valueForUndefinedKey:方法,实际上这个方法就是抛出一个异常,这个异常将会导致程序因为异常结束

4、处理不存在的key

   重写两个方法处理不存在的key,使程序不会因为抛出异常而导致程序结束

   -(void)setVlaue:(id)value forUndefinedKey:(NSString *)key;

  -(id)valueForUndefinedKey:(NSString *)key;


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

    NSLog(@"%@赋值时,%@未找到",key,key);

}

-(id)valueForUndefinedKey:(NSString *)key{

     NSLog(@"%@的值时,%@未找到",key,key);

    return @"test";

}


        [stu setValue:@88 forKey:@"stuScore"];

        NSLog(@"%@",[stu valueForKey:@"stuScore"]);


5、处理nil值

   当调用KVC来设置对象的属性时,如果属性的类型是基本数据类型(如:int、float、double),且程序传入了对应类型的值,那么程序可以正确的设置。如果对基本数据类型的属性设置一个nil,则会报错,程序会引发一个NSInvalidArgumentException异常,这是由于基本数据类型不能接受nil值所导致的。

   要使程序处理nil值,则要重写setNilValueForKey:方法来实现。

-(void)setNilValueForKey:(NSString *)key{

    if ([key isEqualToString:@"stuAge"]) {//_stuAge是int类型的

        _stuAge = 0;

    }else if([key isEqualToString:@"stuSex"]){//_stuSex是BOOL类型的

        _stuSex = NO;

    }else{

        [super setNilValueForKey:key];

    }

}

        [stu setValue:nil forKey:@"stuAge"];

        [stu setValue:nil forKey:@"stuName"];

        NSLog(@"%@,%@",[stu valueForKey:@"stuAge"],[stu valueForKey:@"stuName"]);

       
6、key路径

   KVC除了可操作对象的属性之外,还可以操作对象的复合属性,所谓复合属性,KVC将其称为key路径。比如,Person对象包含一个IdCard类型的ic属性,而IdCard对象又包含了cardNo、cardName属性,那么KVC可以通过ic.cardNo、ic.cardName这种key路径来支持Person对象的ic属性中的cardNo、cardName属性。

   在KVC中操作key路径的方法如下:

   -setValue:forKeyPath:  为指定属性设置值

   -valueForKeyPath:  获取指定属性的值

  例如:在上面例子中添加一个属性   

@property (nonatomic, strong) Dog *dog;

  

@interface Dog : NSObject

@property (nonatomic, copy) NSString *dogName;

@property (nonatomic,assign) int dogAge;

@property (nonatomic, copy) NSString *dogBrand;

@end

        Dog *dog = [Dog new];

        [stu setValue:dog forKey:@"dog"];

        [stu setValue:@"小黑" forKeyPath:@"dog.dogName"];

        NSLog(@"%@",[stu valueForKeyPath:@"dog.dogName"]);

      

7、KVO键值监听

   利用KVO(Key Value Observing,键值监听)机制,当指定对象的属性被修改后,则对象就会收到接受通知。每次指定的被观察者的对象的属性被修改后,KVO就会自动通知相应的观察者。

   KVO是观察者设计模式中的一种应用。KVO由NSKeyValueObserving提供支持,常用方法如下:

   addObserver:forKeyPath:options:context:注册一个监听器用于监听指定的key路径

   observerVlaueForKeyPath:ofObject:change:context:当被监听器的key路径对应的属性值发生改变时,就会回调监听器自身的方法。

   removeObserver:forKeyPath:为key路径删除指定的监听器  

@interface ShopCar : NSObject

@property (nonatomic, assign) int  totalNumber;

@property (nonatomic, assign) float totalPrice;

@end

    _shopCar = [[ShopCar alloc] init];

    _shopCar.totalNumber = 0;

    _shopCar.totalPrice = 0.0f;

    _lbNumber.text = [NSString stringWithFormat:@"%i",_shopCar.totalNumber];

    _lbPrice.text = [NSString stringWithFormat:@"%f",_shopCar.totalPrice];

    

    //使用KVO添加监听

    [_shopCar addObserver:self forKeyPath:@"totalNumber" options:NSKeyValueObservingOptionNew context:nil];

    [_shopCar addObserver:self forKeyPath:@"totalPrice" options:NSKeyValueObservingOptionNew context:nil];


-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{

    //NSLog(@"%@",change);

    if ([keyPath isEqualToString:@"totalNumber"]) {

        _lbNumber.text =  [NSString stringWithFormat:@"%i",[[change objectForKey:@"new"] intValue]];

    }else if ([keyPath isEqualToString:@"totalPrice"]){

        _lbPrice.text =  [NSString stringWithFormat:@"%.2f",[[change objectForKey:@"new"] floatValue]];

    }

}

- (IBAction)addAction:(id)sender {

    _shopCar.totalNumber++;

    _shopCar.totalPrice += 100;

}


- (IBAction)removeAction:(id)sender {

    if (_shopCar.totalNumber > 0) {

        _shopCar.totalNumber--;

        _shopCar.totalPrice -= 100;

    }else{

        _shopCar.totalNumber = 0;

        _shopCar.totalPrice = 0.0f;

    }

}




你可能感兴趣的:(IOS学习笔记之KVC、KVO)