KVC和KVO原理解析

KVC 键值编码(Key-value coding)

KVC是一套方便我们用字符串来操作对象的机制,可以使得操作对象时跟操作字典一样的灵活。在字典转模型的领域中应用起来极为方便,并且KVC可以轻松的帮我们突破访问限制的一些问题,直接访问到私有成员

  • 主要方法
- (id)valueForKey:(NSString *)key;  
- (void)setValue:(id)value forKey:(NSString *)key; 
//如果需要操作访问一些“属性里的属性”时,就用带Path的方法来操作 
- (id)valueForKeyPath:(NSString *)keyPath;  
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
  • KVC的优点
    (1). 无论类中的成员是否私有,用KVC都可以对它们正常取值和赋值
    比如当属性在扩展里面 无法通过点语法赋值 可以通过kvc赋值
    (2). 不管你的成员变量是否加下划线,你用KVC取值和赋值时传入的属性名都可以不带下划线
    (3). 大大简化字典转模型代码,KVC提供了一套更简洁的操作方式,只需你传入一个字典,就可以帮你自动把字典里的每一项赋值给你实体类对应的属性
NSDictionary *dic = @{
                          @"name":@"test",
//KVC中,赋值时传入的值都只能是对象类型,无法直接传入基本数据类型
                          @"age":@18,
                          @"address":@"China"
                          };
    [p setValuesForKeysWithDictionary:dic];
    NSLog(@"name:%@ age:%ld address:%@",p.name,p.age,p.address);
  • KVC缺点:
    1.在编码时很容易输错key导致问题
    解决办法:用KVC时传入的Key必须保证类中存在同名的属性。否则会运行时崩溃。如果不希望运行时直接崩溃,就需要在类里重写setValue:值 forUndefinedKey:键方法,这样,当用KVC对Person对象赋值了一个Key与属性对应不上的错误时,系统会自动调用这个方法.
    2.语法相较点语法而言也略微繁琐。
    解决办法:用runtime取代

  • 实现方法:
    KVC运用了一个isa-swizzling技术,任何对象都有isa指针。KVC主要通过isa-swizzling,来实现其内部查找定位的:
    (1) 实例方法调用时,通过对象的 isa 在类中获取方法的实现
    (2) 类方法调用时,通过类的 isa 在元类中获取方法的实现

  • KVC的赋值原理:

@implementation Person {
    NSString *_address;
}

Person *p  =[[Person alloc] init];
[p setValue:@"China" forKey:@"address"];
NSString *ads = [p valueForKey:@"address"];

找的顺序是 setAddress:==>_address==>_isAddress==>address==>isAddress

  1. 用KVC赋值时,会优先调用set:属性值方法(setAddress:(NSString *)address)
  2. 如果找不到,则会先找带下划线的成员变量_(_address),再找_is(_isAddress),如果找到则赋值
  3. 如果上面都找不到,则会先查找不带下划线的成员变量(address),再找is(isAddress),找到则赋值
  4. 如果上面列出的方法或者成员变量都不存在,系统将会执行该对象的setValue:forUNdefinedKey:方法,默认是抛出异常
    5.如果想让这个类禁用KVC,那么重写+ (BOOL)accessInstanceVariablesDirectly方法让其返回NO即可,这样的话如果KVC没有找到set:属性名时,会直接用setValue:forUndefinedKey:方法.

KVO 键值观察Key-value observing)

  • 实现原理
    当你观察一个对象时,一个新的类会动态被创建。这个类继承自该对象的原本的类,并重写了被观察属性的 setter 方法。自然,重写的 setter 方法会负责在调用原 setter 方法之前和之后,通知所有观察对象值的更改。最后把这个对象的 isa 指针 ( isa 指针告诉 Runtime 系统这个对象的类是什么 ) 指向这个新创建的子类,对象就神奇的变成了新创建的子类的实例。
    原来,这个中间类,继承自原本的那个类。不仅如此,Apple 还重写了 -class 方法,企图欺骗我们这个类没有变,就是原本那个类。

简单的说就是在给某个类的属性添加观察的时候,系统就创建一个该类派生类,然后派生类重写该属性的setter方法实现观察对象的改变,然后将这个对象的 isa 指针指向这个派生类。对象就变成了新创建的子类的实例。
它是基于KVC实现的,实现了响应式编程,内部实现运用了runtime的特性。
下面这篇文章介绍了如何手动实现KVO
http://tech.glowing.com/cn/implement-kvo/

你可能感兴趣的:(KVC和KVO原理解析)