iOS KVO的使用和原理及自定义

一  观察者与被观察者之间的关系

iOS KVO的使用和原理及自定义_第1张图片

观察者self强引用被观察者p,但是被观察者p并不强引用self否则将会造成循环引用.当self被释放了,p拿不到self,但p的属性name仍然可能会被赋值所以observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionaryid> *)change context:(void*)context这个方法仍然可能会被调用而此时self已经被释放了.为了防止这种现象所以要在 delloc里面移除观察者.

二  KVO触发条件

1.

iOS KVO的使用和原理及自定义_第2张图片

如图在Person类中重写+ (BOOL)automaticallyNotifiesObserversForKey:(NSString*)key方法,如果返回NO,KVO为手动触发模式如果返回YES则为自动触发模式. key为受到改变的Person类的属性.这里将改变name时设置为手动触发模式

2.

iOS KVO的使用和原理及自定义_第3张图片

在赋值前添加 [_p willChangeValueForKey:NSStringFromSelector(@selector(name))]; 意为即将做出改变,在赋值后添加[_p didChangeValueForKey:NSStringFromSelector(@selector(name))]; 意为已经做出改变.这两个方法为手动模式触发的条件. 设置完后触发touchesBegan可以看到控制台输出,监听成功.

3.

iOS KVO的使用和原理及自定义_第4张图片

若此时我们不添加给属性name的赋值方法,通过控制台打印发现kvo仍可以被触发.说明KVO触发与属性赋值无关但与willChangeValueForKey及didChangeValueForKey这两个方法的调用有关.

三 属性依赖

iOS KVO的使用和原理及自定义_第5张图片
给Person类新建一个类属性friends
iOS KVO的使用和原理及自定义_第6张图片

给Person类新建一个类属性friends并且观察这个属性,通过touchesBegan改变friends的num及age属性.发现并未触发kvo.因为我们观察的是friends属性而不是_friends.age,那么如何才能在改变friends的属性时也能触kvo模式呢?

iOS KVO的使用和原理及自定义_第7张图片

在person类里重写+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString*)key方法.这里返回的key是我们所观察的person的属性.通过判断当key为friends时返回一个集合,集合里就放入我们所想观察变化的属性如:_friends.age和_friends.num

iOS KVO的使用和原理及自定义_第8张图片

再次改变person的属性,通过控制台打印发现kvo可以被触发.

四  自定义KVO

1.  开发中若想一直操作某个类的属性改变的地方有两种:1.在分类中重写属性setter方法 2.通过子类继承重写父类属性的setter方法(注:子类并不拥有父类属性而是可以调用父类方法). 但是如果我们按照方法1的话就覆盖原来的setter方法造成我们原有setter方法中的操作不被执行,所以方法2是一个很好的选择.那么KVO实现的原理会不会也是这样呢?

iOS KVO的使用和原理及自定义_第9张图片
,

如图可以看到在执行了[_p addObserver:self forKeyPath:@"friends" options:(NSKeyValueObservingOptionNew) context:nil];这句代码后,通过lldb调试发现原来的person对象_p的isa指针不再指向person类而是NSKVONotifying_Person这个类.

2.  添加一个分类NSObject+KVO.h并仿照系统方法给分类添加一个方法DIY_addObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath options:(NSKeyValueObservingOptions)options context:(void*)context;在这个方法中我们新建了一个类继承于self也就是被观察者,并给这个类添加了一个setName方法相当于其父类中的setter方法.并且将被观察者的isa指针指向这个新类(isa指针指向谁,调用方法的就是谁),如此当被观察者调用setName方法时将会执行新类的setName方法(注意这里的方法名称一定是相同的).再在setName方法中给观察者observe的observeValueForKeyPath:ofObject:change:context:这个方法发送消息如此自定义的观察者就实现了! 直接使用对象调用给分类添加的这个方法即可.

iOS KVO的使用和原理及自定义_第10张图片

五  容器类观察

iOS KVO的使用和原理及自定义_第11张图片

给person类添加一个数组array并且观察这个数组的变化,若直接调用_p对象的array属性的addObject方法是无法触发监听者模式的,此时kvo提供了一个方法通过mutableArrayValueForKey获取可变数组并且通过改变这个数组即可触发kvo. 再次通过lldb调试又发现arrays指向了NSKeyValueNotifyingMutableArray这个类,那么KVO会不会又创建了一个继承NSMutableArray的子类NSKeyValueNotifyingMutableArray,再通过重写这个子类的addObject方法实现了监听呢?

你可能感兴趣的:(iOS KVO的使用和原理及自定义)