KVC和KVO

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吗?为什么?

答案和其他问题待更新!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350