KVC:
定义:
KVC全称是Key Value Coding(键值编码),是一个基于NSKeyValueCoding非正式协议实现的机制,它可以直接通过key值对对象的属性进行存取操作,而不需通过调用明确的存取方法。这样就可以在运行时动态在访问和修改对象的属性,而不是在编译时确定。
KVC主要对三种类型进行操作,基础数据类型及常量、对象类型、集合类型
通用方法:
valueForKey:getter方法:
setValue:forKey:setter方法:
forKeyPath:衍生的keyPath方法,用来进行深层访问(key使用点语法),也可单层访问:
valueForKeyPath:
setValue:
KVC 的多值操作:
//批量赋值
NSDictionary *dic = @{
@"name":@"xiaoMing",
@"sex":@1,
@"age":@12,
@"address":myAddress
};
[myself setValuesForKeysWithDictionary:dic];
//批量取值
NSArray *keys = @[@"name",@"age",@"sex",@"address"];
NSDictionary *values = [myself dictionaryWithValuesForKeys:keys];
NSLog(@"%@",values);
原理:
Setter方法:
1、底层会优先调用set<Key>、_set<Key>方法,按照这个先后顺序,且只实现其中一个方法;
2、如果没有上述方法,就会调用accessInstanceVariablesDirectly进行判断,如果返回NO,直接进入下面3步骤,否则YES,调用实例变量赋值顺序_<key>,_is<Key>,<key>,is<Key>,且只实现其中一个方法;
3、如果没有找到存取方法或者实例变量就会调用setValue:forUndefinedKey:方法。
Getter方法:
1.查找set<Key>:或_set<Key>命名的setter,按照这个顺序,如果找到的话,调用这个方法并将值传进去(根据需要进行对象转换)。
2.如果没有发现一个简单的setter,但是 accessInstanceVariablesDirectly 类属性返回YES,则查找一个命名规则为_<key>、_is<Key>、<key>、is<Key>的实例变量。根据这个顺序,如果发现则将value赋值给实例变量。
3.如果没有发现setter或实例变量,则调用setValue:forUndefinedKey:方法,并默认提出一个异常,但是一个NSObject的子类可以提出合适的行为。
程序优先调用setKey:属性值方法,代码通过setter方法完成设置。注意,这里的key是指成员变量名,首字母大小写要符合KVC的命名规范
如果没有找到setName:方法,KVC机制会检查+(BOOL)accessInstanceVariablesDirectly方法有没有返回YES,默认返回的是YES,如果你重写了该方法让其返回NO,那么在这一步KVC会执行setValue: forUndefineKey:方法,不过一般不会这么做。所以KVC机制会搜索该类里面有没有名为_key的成员变量,无论该变量是在.h,还是在.m文件里定义,也不论用什么样的访问修饰符,只要存在_key命名的变量,KVC都可以对该成员变量赋值。
如果该类既没有setKey:方法,也没有_key成员变量,KVC机制会搜索_isKey的成员变量。
同样道理,如果该类没有setKey:方法,也没有_key和_isKey成员变量,KVC还会继续搜索key和isKey的成员变量,再给他们赋值。
如果上面列出的方法或者成员变量都不存在,系统将会执行该对象的setValue:forUndefinedKey:方法,默认是抛出异常。
应用:
使用 KVC 进行字典转模型:
可以使用 setValuesForKeysWithDictionary: 进行字典转模型。
注意点:
Json里的字段数量和字段名字应该和model类所匹配,Json少了字段不会出现问题,但是多了或者是字段名不对,就会崩溃。
不需对基本数据类型做处理,例如int型转NSNumber,内部会自动作出处理
NSArray和NSDictionary等集合对象,value都不能是nil,否则会导致Crash
访问类的私有变量:
例如下面在.m中定义的私有成员变量和属性,都可以通过KVC的方式访问
这个操作对readonly的属性,@protected的成员变量,都可以正常访问。如果不想让外界访问类的成员变量,则可以将accessInstanceVariablesDirectlygetter方法返回为NO
UITabbar或UIPageControl这样的控件样式的改变:
自定义一个UITabbar对象,然后在内部创建自己想要的视图,并通过layoutSubviews方法在内部进行重新布局。然后通过KVC的方式,将UITabbarController的tabbar属性替换为自定义的类即可
其他:
赋值时会遇到一些问题,例如服务器会返回一个id字段,但是对于客户端来说id是系统保留字段,可以重写setValue:forUndefinedKey:方法并在内部处理id参数的赋值
KVO:
KVO是key-value-observe的简称,也就是键值观察者,是一种设计模式 -- 观察者模式。NSKeyValueObserving协议提供支持,NSObject类继承了该协议,所以NSObject的子类都可使用该方法。 核心思想就是:
被观察者的状态发生改变时,会通知给观察者,观察者在对应的方法里可以获取相关信息。
Apple 通过isa混写(isa--swizzling)实现了KVO
原理:
KVO 是基于 runtime 机制实现的 当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一 个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。派生类在 被重写的 setter 方法内实现真正的通知机制。
如果原类为 Person,那么生成的派生类名为 NSKVONotifying_Person 每个类对象中都有一个 isa 指针指向当前类,当一个类对象的第一次被观察,那么 系统会偷偷将 isa 指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的 setter 方法。
键值观察通知依赖于 NSObject 的两个方法willChangeValueForKey:didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey: 一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey: 会被调用,继而observeValueForKey:ofObject:change:context: 也会被调用。
KVO实现步骤:
1.注册观察者(为被观察这指定观察者以及被观察者属性)
observer -- 观察者
keyPath -- 被观察者的“变量”,通常就是被观察对象的属性
options -- 在回调方法中我需要获取到的“变量”值,如新值(NSKeyValueObservingOptionNew)、旧值(NSKeyValueObservingOptionOld)等。
context -- 上下文,我理解的是观察者所在的上下文,用于区分有很多观察者时,进行分类处理。传nil也可以。
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
2.实现回调方法
//当key路径对应的属性值发生改变时,监听器就会回调自身的监听方法,如下-(void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id>*)change context:(void*)contex}
3.触发回调方法
4.移除观察者
//删除指定的key路径监听器
//移除观察者
- (void)dealloc {
[self.myKVO removeObserver:self forKeyPath:@"num" context:nil];
}
//删除指定的key路径监听器,只是多了context参数
[self.person removeObserver:self forKeyPath:@"name" context:nil];
然后 关闭自动KVO,实现代码如下:
+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
if ([key isEqualToString:@"secondString"]) {
return NO;
}
return [super automaticallyNotifiesObserversForKey:key];
}
这个方法是系统提供的,在调用addObserver:forkeyPath:options:context:方法时,就会走到这个方法里面,当对应的keyPath 进到这个方法时,返回NO,那就是关闭自动KVO;同时注意,除此之外的所有keyPath 都让它走父类的方法,这样就不会影响其他的KVO。
KVO的主要应用场景:
1.当数据模型的数据发生改变时,视图组件能动态的更新,及时显示数据模型更新后的数据。
比如:监听scrollView的contentOffset属性,来完成用户滚动时动态改变某些控件的属性实现效果,包括渐变导航栏、下拉刷新控件等效果。
2. 监控collectionview的frame和底层的scrollview的contentsize太小来实现动态变化
面试题:
1.KVO的使用?实现原理?(为什么要创建子类来实现)
2.KVC的使用?实现原理?(KVC拿到key以后,是如何赋值的?知不知道集合操作符,能不能访问私有属性,能不能直接访问_ivar)
3. addObserver:forKeyPath:options:context:各个参数的作用分别是什么,observer中需要实现哪个方法才能获得KVO回调?
//添加键值观察
/ *
1观察者,负责处理监听事件的对象
2观察的属性
3观察的选项
4上下文
* /
[自 .person的addObserver:自 forKeyPath:@ “名称” 选项:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld 上下文:@“人员名称” ];
观察者中需要实现一下方法:
//所有的kvo监听到事件,都会调用此方法
/ *
1.观察的属性
2。观察的对象
3。change属性变化字典(新/旧)
4.上下文,与监听的时候传递的一致
* /
-( void)observeValueForKeyPath :( NSString *)ObjectPath的keyPath :( id)object change :( NSDictionary *)change context :( void *)上下文;
4.如何手动触发一个value的KVO?
想知道如何手动触发,必须知道自动触发KVO的原理:
键值观察通知依赖于NSObject的两个方法: willChangeValueForKey:和didChangevlueForKey:。在一个被观察属性发生改变之前, willChangeValueForKey:一定会被调用,然后会会记录旧的值。而当改变发生后, observeValueForKey:ofObject:change:context:会被调用,继而也会didChangeValueForKey:被调用。如果可以手动实现这些调用,就可以实现“手动触发”了。
那么“手动触发”的使用场景是什么?一般我们只在希望能控制“放置的调用时机”时才会这么做。
具体做法如下:
如果这个 value是表示时间的self.now,那么代码如下:最后两行代码缺一不可。
5.若一个类有实例变量 NSString *_foo ,调用setValue:forKey:时,可以以foo还是 _foo 作为key?
都可以。
6.KVC的keyPath中的集合运算符如何使用?
必须用在集合对象上或普通对象的集合属性上
简单集合运算符有@avg,@count,@max,@min,@ sum,
格式@“ @ sum.age”或@“集合属性。@ max.age ”
7. KVC和KVO的keyPath一定是属性么?
KVC支持实例变量,KVO只能手动支持手动设置实例变量的KVO实现监听
8.如何关闭默认的KVO的默认实现,并进入自定义的KVO实现?
9.apple用什么方式实现对一个对象的KVO?
自动键值观察是使用称为isa-swizzling ...的技术实现的。当为观察者注册对象的属性时,将修改被观察对象的isa指针,指向中间类而不是真实类。 ..
当您观察一个对象时,一个新的类会被动态创建。这个类继承自该对象的原本的类,并转换为被观察属性的setter方法。转化的setter方法会负责在调用原setter方法之前和之后,通知所有观察对象:值的更改。最后通过 isa 混写(isa-swizzling)把这个对象的isa指针(isa指针告诉Runtime系统这个对象的类是什么)指向这个新创建的子类,对象就神奇了变成了新创建的子类的实例。
10.NSNotification和KVO的区别和用法是什么?什么时候应该使用通知,什么时候应该使用KVO,它们的实现上有什么区别吗?如果用protocol和delegate(或者delegate的Array)来实现类似的功能可能吗?如果可能,会有什么潜在的问题?如果不能,为什么?
11.setvalue forkey 和 setobject forkey的区别?
(1) setValue:forKey: 的value是可以为nil的(但是当value为nil的时候,会自动调用removeObject:forKey方法);
setObject:forKey: 的value则不可以为nil。
(2) setValue:forKey: 的key必须是不为nil的字符串类型;
setObject:forKey: 的key可以是不为nil的所有类型。
12.Notification的使用场景是什么?同步还是异步?
13.如果让你实现 NSNotificationCenter,讲一下思路
14. KVO、Notification、delegate 各自的优缺点,效率还有使用场景?
15. 通过KVC可以触发KVO吗?为什么?
会
答案和其他问题待更新!!!!!!!!!!!!!!!!!!!!!!!!!!!!!