您的位置:首页 > 其它

KVC,KVO,通知

2016-02-16 15:57 239 查看
KVO就是键值观察

KVC就是键值编码, 间接访问对象实例变量的方式, 该机制可以不通过存取方法就可以访问对象的实例变量.

KVO 是建立在 KVC的基础上的, 在KVO中必须使用键值编码才能修改他的实例变量.

手动实现kvo: http://tech.glowing.com/cn/implement-kvo/

      http://blog.sunnyxx.com/2014/03/09/objc_kvo_secret/

举个KVO的例子 : 

- (void)viewDidLoad {

    [superviewDidLoad];

    

    UIButton *btn = [[UIButtonalloc]initWithFrame:CGRectMake(100,500,100,100)];

    self.btn = btn;

    btn.backgroundColor = [UIColorredColor];

    [self.viewaddSubview:btn];

    

    KVO用来监听属性的变化,有人改变了btn的属性,我就会监听到

    只要item的forKeyPath监听的值改变了,addObserver就会监听到(调用self的一个方法进行监听的操作)

    [btn addObserver:@"注册的所要监控的对象(观察者, 负责处理监听事件的对象, 我监控btn的属性的变化)" forKeyPath:@"所要监控的属性"
options:@"观察的选项 (观察什么的变化(比如

NSKeyValueObservingOptionNew和NSKeyValueObservingOptionOld))" context:@"传递给监控者的任意数据"];

//添加键值观察

1. 观察者, 负责处理监听事件的对象

2. 观察的属性

3. 观察的选项

4. 上下文

    [btn addObserver:self forKeyPath:@"highlighted" 

options:NSKeyValueObservingOptionNew context:@"test_Button"];

}

//所有KVO监听到事件,
都要调用下面的方法

1. 观察的属性

2. 观察的对象

3. change 属性变化字典

4. 上下文, 与监听的时候传递的一致

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString
*,id> *)change context:(void *)context {

   //防止注册了多个KVO的这种情况

    if((self.btn == object) && ([keyPathisEqualToString:@"highlighted"]))
{

    

        NSLog(@"keyPath= %@, object= %@, change= %@, context= %@", keyPath, object, change, context);

    } else {

        //
有可能绑定的是父类的KVO

        [super
observeValueForKeyPath:keyPath ofObject:objectchange:changecontext:context];

    }

}

-(void)dealloc {

    // test_Button,防止父类释放了,自己又释放而引起的奔溃问题.

    [self.btn removeObserver:self forKeyPath:@"highlighted" context:@"test_Button"];

}

我写的不清楚的地方, 可以参考: http://www.cnblogs.com/wengzilin/p/4346775.html

KVC: 

1. setValue: forKey   只能设置属性,实例变量

2. setValue: forKeyPath  可以对他们的路径进
12976
行设置 @"btn.highlighted"

3. KVC 的本质就是查找属性,实例变量,通过isa指针去查找(isa会告诉系统这个对象的类是什么). 先在子类, 再在父类, 如果最终没有找到这个属性, 就检查有没有实现btn setValue: forUndefinedKey:这个方法, 如果没有实现,就会奔溃.

4. KVC的本质就是遍历字典中所有的key, 去模型中查找对应的属性, 然后把值赋给模型属性

[dict enumerateKeysAndObjectsUsingBlock:

^(id _Nonnull key,id _Nonnull obj,BOOL
*_Nonnull stop) {

       //这行代码才是真正给模型的属性赋值

        [s setValue:dict[@"name"] forKey:@"name"];

查找过程: 首先在模型中查找有没有setName方法, 找到之后[s setName:dict[@"name"]]

找不到就在模型中寻找有没有name属性, 没有找到就寻找_name, 

最后实在找不到就调用setValue:forUndefinedKey:方法, 如果没有实现此方法, 系统自动奔溃.

}];

KVO和通知的区别:

1)KVO:(Key Value Observing)

被观察者发出 addObserver:forKeyPath:options:context: 方法来添加观察者。

然后只要被观察者的keyPath值变化(注意:单纯改变其值不会调用此方法,只有通过getters和setters来改变值才会触发KVO),就会在观察者里调用方法observeValueForKeyPath:ofObject:change:context:

因此观察者需要实现方法 observeValueForKeyPath:ofObject:change:context: 来对KVO发出的通知做出响应。
这些代码都只需在观察者里进行实现,被观察者不用添加任何代码,所以谁要监听谁注册,然后对响应进行处理即可,使得观察者与被观察者完全解耦,运用很灵活很简便;但是KVO只能检测类中的属性,并且属性名都是通过NSString来查找,编译器不会帮你检错和补全,纯手敲所以比较容易出错,最好用宏定义。
2)NSNotification这里的通知不是由被观察者发出,而是由NSNotificationCenter来统一发出,而不同通知通过唯一的通知标识名notificationName来区分,标识名由发送通知的类来起。首先被观察者自己在必要的方法A里,通过方法postNotificationName:object:来发出通知notificationName这样发送通知者这边的工作就完成了,每次A被调用,就会发送一次通知notificationName。然后谁要监听A的变化,就通过[NSNotificationCenterdefaultCenter]的方法addObserver:selector:name:object:为观察者注册监听name为notificationName的通知然后每次发出name为notificationName的通知时,注册监听后的观察者就会调用其自己定义的方法notificationSelector来进行响应。
NSNotification的特点呢,就是需要被观察者先主动发出通知,然后观察者注册监听后再来进行响应,比KVO多了发送通知的一步,但是其优点是监听不局限于属性的变化,还可以对多种多样的状态变化进行监听,监听范围广,使用也更灵活。

通知的使用

1. 必须先监听通知

2. 然后发送通知, 只要是一个频道, 就都是可以监听到通知.

?
3. 通知传值

发送通知, 将要传递的数值包装成一个字典, 传递过去.

如果我们要传递一个对象, 直接在object后面传递数据就可以了; 如果要传递很多组数据, 那就在userInfo后面传递就行了

NSNotification *notification =[NSNotification notificationWithName:@"PJBaseSelectView" object:nil userInfo:@{@"PJMe":model,@"PJMeFlag":STRING_Int(0)}];

[[NSNotificationCenter defaultCenter] postNotification:notification];

接收通知, 接收到数值之后, 再解析出字典里面的数值

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(tongzhiBaseSelect:) name:@"PJBaseSelectView" object:nil]; 

- (void)tongzhiBaseSelect:(NSNotification *)text {

    

    PJLog(@"%@", text);

    

    NSString *flagValue = text.userInfo[@"PJMeFlag"];

    HTHotSearchModel *model = text.userInfo[@"PJMe"];

}

KVO实现
这是怎么实现的呢?其实这都是通过Objective-C强大的运行时(runtime)实现的。当你第一次观察某个object 时,runtime会创建一个新的继承原先class的subclass。在这个新的class中,它重写了所有被观察的key,然后将object的isa指针指向新创建的class(这个指针告诉Objective-C运行时某个object到底是哪种类型的object)。所以object神奇地变成了新的子类的实例。

这些被重写的方法实现了如何通知观察者们。当改变一个key时,会触发setKey方法,但这个方法被重写了,并且在内部添加了发送通知机制。(当然也可以不走setXXX方法,比如直接修改iVar,但不推荐这么做)。

有意思的是:苹果不希望这个机制暴露在外部。除了setters,这个动态生成的子类同时也重写了-class方法,依旧返回原先的class!如果不仔细看的话,被KVO过的object看起来和原先的object没什么两样。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: