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