KVC
Objective-C中有个显式的NSKeyValueCoding类别名,所以对于所有继承了NSObject的类型,都能使用KVC.
KVC的找寻方式
setter,getter方法,_name,name
KVC来访问和修改私有变量
对类里的私有属性,Objective-C是无法直接访问的,但KVC是可以的。(嗯。这个功能很强大)。Model和字典的转换
KVC和Object的runtime组合可以很容易实现Model和字典的转换修改一些控件的内部属性
比如在很多UI控件都有很多内部UI控件组合而成的。但是Apple没有提供这些控件的API。这样我们就无法正常的访问和修改控件的样式。
KVO
Objective-C中有个显式的NSKeyValueObserving类别名,所以对于所有继承了NSObject的类型.
- 关闭KVO
+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key { return NO; }
KVO的行为是同步的
发生与所有观察的值发生变化的同样的线程上。
KVO的同步运行这个特性是非常强大的,只要我们在单一线程上面运行,KVO就能保证setter方法在执行完之前被通知到。KVO的实现
实现思路
编译器自动为被观察的对象创造一个派生类,并将被观察对象的isa指向这个派生类。若果用户注册了对某个目标对象的某一个属性的观察,那么此派生类会重写这个方法,并在其中添加通知的代码。Object-c在发送消息的时候,会通过isa指针找到当前对象所属的类对象。而类对象中保存着当前对象的实例方法。因此在向此对象发送消息时候,实际上是发送到了派生类对象的方法。又由于编译器对派生类的方法进行了重写,并添加了通知代码,因此会向注册的对象发送通知。
#import "NSObject+RAOKVO.h"
#import <objc/message.h>
const char *key;
@implementation NSObject (RAOKVO)
- (void)rao_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
/*
动态创建一个新类
*/
NSString *oldName = NSStringFromClass([self class]);
NSString *newName = [@"RAO" stringByAppendingString:oldName];
Class myClass = objc_allocateClassPair([self class], [newName UTF8String], 0);
objc_registerClassPair(myClass);//注册类(相当于加载吧)
object_setClass(self, myClass);//更改本类类型(修改isa指向)
//重写setName,实际是给子类添加方法(因为如果本类没方法,实际是找到父类方法)
class_addMethod(myClass, @selector(setName:), (IMP)setName,"v@:@");
//将观察者绑定到子类对象
objc_setAssociatedObject(self, @"key", observer, OBJC_ASSOCIATION_ASSIGN);//(用ASSIGN防止循环引用)
}
void setName(id self,SEL sel,NSString * name) {
struct objc_super person = {self,class_getSuperclass([self class])};
objc_msgSendSuper(&person,sel,name);
//拿出观察者
id observer = objc_getAssociatedObject(self,@"key");
objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),"name",self,@{@"name":name});
}
@end