KVC(Key-value coding)键值编码,指iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。不需要调用明确的存取方法。这样就可以在运行时动态地访问和修改对象的属性。而不是在编译时确定,这也是iOS开发中的黑魔法之一。
KVC可以不通过getter/setter方法来访问对象的属性。
因为一个类的成员变量如果没有提供getter/setter的话,外界就失去了对这个变量的访问渠道。而KVC则提供了一种访问的方法,这个在某些场合会很有威力。例如,直接修改系统控件的内部属性
KVO 是 Objective-C 对观察者设计模式的一种实现。【另外一种是:通知机制(notification)】;
KVO提供一种机制,指定一个被观察对象(例如A类),当对象某个属性(例如A中的字符串name)发生更改时,监听对象会获得通知,并作出相应处理;【且不需要给被观察的对象添加任何额外代码,就能使用KVO机制】
在MVC设计架构下的项目,KVO机制很适合实现mode模型和view视图之间的通讯。
KVC(Key-value coding)
下面是KVC最为重要的四个方法
- (nullable id)valueForKey:(NSString *)key; //直接通过Key来取值
- (void)setValue:(nullable id)value forKey:(NSString *)key; //通过Key来设值
- (nullable id)valueForKeyPath:(NSString *)keyPath; //通过KeyPath来取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; //通过KeyPath来设值
+ (BOOL)accessInstanceVariablesDirectly;
//默认返回YES,表示如果没有找到Set<Key>方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索
- (nullable id)valueForUndefinedKey:(NSString *)key;
//如果Key不存在,且没有KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常。
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
//和上一个方法一样,但这个方法是设值。
- (void)setNilValueForKey:(NSString *)key;
//如果你在SetValue方法时面给Value传nil,则会调用这个方法
修改系统控件的内部属性(Runtime+KVC)
比如修改tabbar底部控制栏的tabbar为自定义tabbar类型,就可以通过kvc来实现。
有时候一些属性是私有变量,可以通过runtime来查找属性值
/**
* 运行时(runtime),获取所有成员变量
*/
- (void)getMemberVariables
{
unsigned ivarCount;
Ivar *ivars = class_copyIvarList([UIPageControl class], &ivarCount);
for (int i = 0; i < ivarCount; i ++) {
NSString *varibale = [NSString stringWithUTF8String:ivar_getName(ivars[i])];
NSLog(@"%@",varibale);
}
}
通过KVC来修改
UIPageControl *pageControl = [[UIPageControl alloc ] init];
[pageControl setValue:[UIImage imageNamed:%@"nomal.png"] forKey:@"_pageImage"];
[pageControl setValue:[UIImage imageNamed:%@"selected.png"] forKey:@"_currentPageImage"]
KVC底层原理分析
setValue:forKey:赋值原理如下
- 去模型中查找有没有对应的setter方法:例如:setIcon方法,有就直接调用这个setter方法给模型这个属性赋值[self setIcon:dic[@"icon"]];
- 如果找不到setter方法,接着就会去寻找有没有icon属性,如果有,就直接访问模型中的icon属性,进行赋值,icon=dict[@"icon"];
- 如果找不到icon属性,接着又会去寻找_icon属性,如果有,直接进行赋值_icon=dict[@"icon"];
- 如果都找不到就会报错:[<Flag 0X7fb74bc7a2c0> setValue:forUndefinedKey:]
- 如果对某个类,不允许使用KVC,可以通过设置 accessInstanceVariablesDirectly 控制。
KVC内部的实现
比如说如下的一行KVC的代码:
[site setValue:@"sitename" forKey:@"name"];
就会被编译器处理成:
SEL sel = sel_get_uid ("setValue:forKey:");
IMP method = objc_msg_lookup (site->isa,sel);
method(site, sel, @"sitename", @"name");
这下KVC内部的实现就很清楚的清楚了:一个对象在调用setValue的时候,
- (1)首先根据方法名找到运行方法的时候所需要的环境参数。
- (2)他会从自己isa指针结合环境参数,找到具体的方法实现的接口。
- (3)再直接查找得来的具体的方法实现。
/
/
/
/
/
KVO
KVO 是 Objective-C 对观察者设计模式的一种实现。【另外一种是:通知机制(notification)】;
KVO提供一种机制,指定一个被观察对象(例如A类),当对象某个属性(例如A中的字符串name)发生更改时,监听对象会获得通知,并作出相应处理;【且不需要给被观察的对象添加任何额外代码,就能使用KVO机制】
在MVC设计架构下的项目,KVO机制很适合实现mode模型和view视图之间的通讯。
notification比KVO多了发送通知的一步。
两者都是一对多,但是对象之间直接的交互,notification明显得多,需要notificationCenter来做为中间交互。而KVO如我们介绍的,设置观察者->处理属性变化,至于中间通知这一环,则隐秘多了,只留一句“交由系统通知”,具体的可参照以上实现过程的剖析。
notification的优点是监听不局限于属性的变化,还可以对多种多样的状态变化进行监听,监听范围广,例如键盘、前后台等系统通知的使用也更显灵活方便。
与delegate的不同
和delegate一样,KVO和NSNotification的作用都是类与类之间的通信。但是与delegate不同的是:
这两个都是负责发送接收通知,剩下的事情由系统处理,所以不用返回值;而delegate 则需要通信的对象通过变量(代理)联系;
delegate一般是一对一,而这两个可以一对多。
KVO应用过程
1:注册观察者,实施监听;
[self.model addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
- observer 指观察者
- keyPath 表示被观察者的属性
- options 决定了提供给观察者change字典中的具体信息有哪些。 【见options解析】
- context 这个参数可以是一个 C指针,也可以是一个 对象引用,它可以作为这个context的唯一标识,也可以提供一些数据给观察者。因为你传进去是啥,回调时候还是回传的还是啥
注册监听,options入参是个枚举,该入参跟监听回调中的change呼应。。并且,以上options入参时候是可以用 | 或运算进行多选的。
例如:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld。。那在change字典中就会包含属性变化前后的值。
2:监听回调
-observeValueForKeyPath:ofObject:change:context:就这一个方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
- keyPath:你所观察对象的属性
- object:你所观察的对象
- change:你所观察对象属性值的变化
}
注意:通过多options监听属性的时候,例如上,并不是回到一次老值,再回调一次新值,,而是新老值都是在change字典中的。。
3:移除观察者
你可以通过 removeObserver:forKeyPath: 或 removeObserver:forKeyPath:context: 方法来移除一个观察。
注意:如果你的 context 是一个 对象,你必须在移除观察之前持有它的强引用。当移除了观察后,观察者对象再也不会受到这个 keyPath 的通知。
KVO的实现原理
当观察某对象 A 时,KVO 机制动态创建一个对象A当前类的子类,并为这个新的子类重写了被观察属性 keyPath 的 setter 方法。setter 方法随后负责通知观察对象属性的改变状况。
参考:
KVC
KVC的理解、应用及其底层原理 https://www.jianshu.com/p/9839ecd75377
iOS开发技巧系列---详解KVC(我告诉你KVC的一切)https://www.jianshu.com/p/9839ecd75377
KVO
网友评论