iOS学习之KVO的使用

来源:互联网 发布:windows md文件编辑器 编辑:程序博客网 时间:2024/04/27 21:33

坚持 成长 每日一篇

在MVC的设置模式下,Model块要通知到Control块的通信我们往往采用的“盲”通信(即不知道对方是什么对象),这个时候我们往往采用的是KVO和Notification,这两者其实是极其相似的。KVO符合了观察者设计模式。

KVO的注册,解除,回调函数

当我们要对model的某个属性添加观察者,可以通过addObserver方法

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;

解除观察者用

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

观察者回调函数,else是把当前不能处理的key值转给父类处理,@”_model”必须是addserver时候传入的字符串,由于字符串的存储的内存的位置栈里,所以此处的==可以成立,如果你传递的是对象的话需要另行处理

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{    if (context == @"_model") {        NSLog(@"key = %@,dic = %@",keyPath,change);    } else {        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];    }}

注意:addObeserver时候最好使用字符串常量,否则就通过key来判断。

我们使用Notification时候,可以通过post来通知观察者作出回调,那么KVO的post类型可以分为两种,一种是系统自动完成post代码添加和我们手动完成post代码加添,我们称为自动实现KVO和手动实现KVO

自动实现KVO

NSObject,NSArray,NSSet均实现了addOberserver和removeOberserverf方法,因此我们不仅可以观察普通对象,还可以观察数组或结合类对象。
当我们使用addOberserver时候,系统自动帮添加了post代码

[model addObserver:observer         forKeyPath:@"age"            options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld            context:@“model”];

当model对象的全局调用setAge:给_age或者age全局变量赋值时候会自动通知观察者(即自动post)

注意:如果setAge里面操作的不是名为age或_age全局变量,且没有通过@synthesize age = _name;来转化控制会crash,使用synthesize会对一个全局变量添加两个监控key值

自动实现KVO只有通过KVC,属性赋值,和set方法背掉用时候会触发post(其实最终都是走set方法)

手动实现KVO

自动实现KVO只有依赖set方法来触发,如果我们在一个方法里面通过如下方式给model的_name全局变量赋值

-(void)upLoad{    _name = @"张三";}

这时候我们的Model发生了改变,但是自动实现KVO并不会通知观察者属性发生变化,如果我们要通知观察者属性发生变化需要加入下面两行代码

-(void)upLoad{   [self willChangeValueForKey:@"name"];   _name = @"张三";   [self didChangeValueForKey:@"name"];}

注意:手动KVO和自动KVO不能同时用,即不能在set方法下加入手动KVO,否则会引起两次KVO函数回调
如果你非要这么做的话,可以在automaticallyNotifiesObserversForKey类方法里面屏蔽对某个key的自动KVO,这样就不会引起观察者两次回调KVO函数

+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key {    if ([key isEqualToString:@"name"]) {        return NO;    }    return [super automaticallyNotifiesObserversForKey:key];}

这里注意:对其它非手动实现的 key,要转交给 super 来处理。

依赖键

一个model可以拥有其他model对象,当我们这个model对象的某个属性如name发生变化时候,我们又如何通知观察者呢,因此Object引入了依赖键

在model的实现文件下添加如下代码

- (NSString *)information{    return [[NSString alloc] initWithFormat:@"%@#%@", [_personModel name], [_personModel infomation]];}- (void)setInformation:(NSString *)theInformation{    NSArray * array = [theInformation componentsSeparatedByString:@"#"];    _personModel.name = array[0];    _personModel.infomation = array[1];}+(NSSet*)keyPathsForValuesAffectingInformation{    return [NSSet setWithObjects:@"personModel.name",@"personModel.infomation", nil];}//+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key//{//    NSSet * keyPaths = [super keyPathsForValuesAffectingValueForKey:key];//    NSArray * moreKeyPaths = nil;////    if ([key isEqualToString:@"personModel"])//    {//        moreKeyPaths = [NSArray arrayWithObjects@"personModel.name",@"personModel.infomation", nil];//    }////    if (moreKeyPaths)//    {//        keyPaths = [keyPaths setByAddingObjectsFromArray:moreKeyPaths];//    }//    //    return keyPaths;//}

使用例子

 [_model addObserver:self forKeyPath:@"information" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld  context:@"_model"];    _model.personModel = [[PersonModel alloc] init];    _model.personModel.name = @"李四";    _model.personModel.infomation = @"福建人";

观察者回调函数

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{    if (context == @"_model") {        NSLog(@"key = %@,dic = %@",keyPath,change);    } else {        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];    }}

控制台输出结果如下

2015-12-25 15:09:13.479 KVO学习Demo[13788:6671887] key = information,dic = {    kind = 1;    new = "\U674e\U56db#(null)";    old = "(null)#(null)";}2015-12-25 15:09:13.479 KVO学习Demo[13788:6671887] key = information,dic = {    kind = 1;    new = "\U674e\U56db#\U798f\U5efa\U4eba";    old = "\U674e\U56db#(null)";}

每次personModel的属性值发生变化都会引起回调函数回调

KVO的底层实现

KVO底层实现机制才用了Runtime机制,大致原理是在掉用addServer时候系统会根据模型类创建一个观察者类来替换原来的类,然后把原来的类对象的isa指向新创建的类里。有兴趣的同学可以看下面的文章,或问度娘,谷哥。。。

http://blog.csdn.net/kesalin/article/details/8194240

总结:KVO简单的页面逻辑使用还想,复杂了不好用,最好自己写一KVO类似的机制来通信

0 0
原创粉丝点击