美文网首页
观察者设计模式

观察者设计模式

作者: 天蓬大元 | 来源:发表于2018-02-28 18:17 被阅读0次
观察者设计模式有两个类型:
1,通知机制(notification)
2,KVO机制

一,通知机制(notification)

通知:A类通知B类执行某段代码,同时将必要的数据传递给B类。
这里面有2个问题需要思考:
1,B类怎么根据A类发送的通知执行特定代码,参数如何得到?
2,A类如何发送通知(目标类是谁,参数如何传递)?
OK,我们直接说代码怎么敲。不去过多的关注细节,先学会如何使用。用熟练了,再来关注细节。
首先,我们在B类的init初始化方法中注册通知中心
if ([self respondsToSelector:@selector(notification:)]) {
    [[NSNotificationCenter defaultCenter] addObserver:self //通知中心注册对象
                                             selector:@selector(notification:) //接收到通知时需要执行的代码块
                                                 name:@"changeValue" //通知中心的名字
                                               object:nil];//参数
}
respondsToSelector这个方法起到什么作用?
为什么在init初始化方法中注册通知中心?
通知中心可以设置名字的用途是什么?
其次,我们要实现对应的代码块方法
- (void)notification:(NSNotification *)noti//noti.object就是我们要获取的数据
{
    NSLog(@"noti == %@",noti.object);
}
最后,我们要在self对象消失时,移除所有的控制中心
- (void)dealloc
{
    //移除当前对象所有的通知中心
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    //移除当前指定频道的通知中心
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"changeValue" object:nil];
}
当对象消失时,为什么要移除所有通知?
B类中所有相关的代码就敲完了
然后在A类中需要干什么呢?发送通知!!
[[NSNotificationCenter defaultCenter] postNotificationName:@"changeValue" //名字
                                                    object:@"hello,world"];//数据
有没有发现发送通知时,根本就没有提到B类。

代码写到这里,A类通知B类执行某段代码,同时将必要的数据传递给B类的代码就写完了。如果还不知道怎么用,就看一下我github上的 Dome吧!

二,KVO机制

KVO :A类观察B类,当B类对象某个属性(例如name)发生变化时,A类对象会获得通知,并作出相应处理
这里面也有2个问题需要思考:
1,A类和B类如何建立观察关系?
2,获得通知后如何做出处理
OK,我们也直接说代码怎么敲。
首先,我们在A类中创建一个B类对象
_b = [[B alloc]init];
然后,将b对象注册给self
[_b addObserver:self//A类的一个对象
    forKeyPath:@"name"//b对象的某个需要观察的属性
       options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld//需要传递的数据(属性的旧值和新值)
       context:nil];
接着,当b对象的name属性发生变化时,a对象执行对应的代码块。我们在A类中实现下面这个方法
- (void)observeValueForKeyPath:(NSString *)keyPath //监听的属性
                      ofObject:(id)object//监听对象
                        change:(NSDictionary *)change //新值和旧值
                        context:(void *)context
{
    //参数一:keyPath 监听对象的属性
    if ([keyPath isEqualToString:@"name"]) {
        //参数二:object代表监听的对象
         B *b2 = (B*)object;
         NSLog(@"name:%@",b2.name);
    }
    //参数三:change 新值和旧值
    NSLog(@"change == %@",change);
}
最后,移除观察
- (void)dealloc
{
    [_b removeObserver:self forKeyPath:@"name"];
}
OK,A类的代码就这么多
那接下来我们看B类中的代码
色即是空,空即是色
不用写任何代码???

好,接下来我们讲点有意思的事情。

好,接下来我们讲点有意思的事情。

好,接下来我们讲点有意思的事情。

A类观察B类,当B类对象某个属性(例如name)发生变化时,A类对象会获得通知,并作出相应处理
仔细想想这段话,获取通知的触发条件是属性变化,那么,怎么让属性值变化呢?
1,直接赋值:_name = @"张辽";
2,通过setter方法赋值
3,通过kvc赋值
并不是所有的方式都会触发通知,只有2或者3发生时,才会触发通知
[b setName:@"张辽"];
[b setValue:@"张辽" forKey:@"name"];

对比通知机制和KVO机制,两者有什么相同点和不同点?在实际的工作当中又该如何去使用呢?

系统的KVO并不是完美的,有一个FB开源的库,好用。介绍给大家 KVOController

番外篇:

一,当通过setter方法赋值时,为什么会触发KVO
基本原理:

当观察对象 _b 时,KVO 机制动态创建一个对象_b当前类的子类,并为这个新的子类重写了被观察属性 keyPath 的 setter 方法。setter 方法随后负责通知观察对象属性的改变状况。

深入剖析:

Apple 使用了 isa 混写(isa-swizzling)来实现 KVO 。当观察对象_b时,KVO机制动态创建一个新的名为:NSKVONotifying_B 的新类,该类继承自对象_b的本类,且 KVO 为 NSKVONotifying_B 重写观察属性的 setter 方法,setter 方法会负责在调用原 setter 方法之前和之后,通知所有观察对象属性值的更改情况。
(备注: isa 混写(isa-swizzling)isa:is a kind of ; swizzling:混合,搅合;)
①NSKVONotifying_B 类剖析:在这个过程,被观察对象的 isa 指针从指向原来的 B 类,被 KVO 机制修改为指向系统新创建的子类 NSKVONotifying_B 类,来实现当前类属性值改变的监听;
所以当我们从应用层面上看来,完全没有意识到有新的类出现,这是系统“隐瞒”了对 KVO 的底层实现过程,让我们误以为还是原来的类。但是此时如果我们创建一个新的名为“NSKVONotifying_B”的类,就会发现系统运行到注册 KVO 的那段代码时程序就崩溃,因为系统在注册监听的时候动态创建了名为 NSKVONotifying_B的中间类,并指向这个中间类了。
(isa 指针的作用:每个对象都有 isa 指针,指向该对象的类,它告诉 Runtime 系统这个对象的类是什么。所以对象注册为观察者时,isa 指针指向新子类,那么这个被观察的对象就神奇地变成新子类的对象(或实例)了。) 因而在该新子类的对象上对 setter 的调用就会调用已重写的 setter,从而激活键值通知机制。
—>我猜,这也是 KVO 回调机制,为什么都俗称KVO技术为黑魔法的原因之一吧:内部神秘、外观简洁。
②子类setter方法剖析:KVO 的键值观察通知依赖于 NSObject 的两个方法:willChangeValueForKey:和 didChangevlueForKey:,在存取数值的前后分别调用 2 个方法:
被观察属性发生改变之前,willChangeValueForKey:被调用,通知系统该 keyPath 的属性值即将变更;当改变发生后, didChangeValueForKey: 被调用,通知系统该 keyPath 的属性值已经变更;之后, observeValueForKey:ofObject:change:context: 也会被调用。且重写观察属性的 setter 方法这种继承方式的注入是在运行时而不是编译时实现的。
KVO 为子类的观察者属性重写调用存取方法的工作原理在代码中相当于:

-(void)setName:(NSString *)newName{ 
    [self willChangeValueForKey:@"name"];    //KVO 在调用存取方法之前总调用 
    [super setValue:newName forKey:@"name"]; //调用父类的存取方法 
    [self didChangeValueForKey:@"name"];     //KVO 在调用存取方法之后总调用
}

疑点:

1,子类中如果直接调用父类的setter方法赋值,还会不会触发KVO。
2,既然KVC可以避开setter方法和getter方法,那KVC就不可能通过子类的setter方法触发KVO,那么KVC到底是怎么触发KVO的。KVC和KVO的底层原理到底是什么?(使用KVC改变属性值时,会自动调用willChangeValueForKey和didChangeValueForKey方法,也就是说,KVO的实现其实是这两个方法被调用后的结果?)
参考资料来自简友iOS开发 -- KVO的实现原理与具体应用一文中,感谢分享。

二,讲了这么半天,KVC到底是个什么东西

KVC(键值编码),即 Key-Value Coding,一个非正式的 Protocol,使用字符串(键)访问一个对象实例变量的机制。而不是通过调用 Setter、Getter 方法等显式的存取方式去访问。

三,KVC和setter 与getter方法相比,那个更好一点?

相比直接访问KVC的效率会稍低一点,所以只有当你非常需要它提供的可扩展性时才使用它。

相关文章

  • 观察者设计模式

    每周学点Java设计模式__观察者设计模式 本次继续更新java23中设计模式之一——观察者模式。 观察者模式(有...

  • 设计模式02-观察者者设计模式

    [toc] 设计模式02-观察者者设计模式 主要来源Head First设计模式(书)观察者设计模式是JDK中使用...

  • RxJava基础—观察者模式

    设计模式-观察者模式 观察者模式:观察者模式(有时又被称为发布(publish )-订阅(Subscribe)模式...

  • RxJava设计模式与原理

    标准观察者设计模式 RxJava是一种特殊的观察者模式,首先我们先来看标准的观察者设计模式。在标准观察者模式中,存...

  • 11.9设计模式-观察者模式-详解

    设计模式-观察者模式 观察者模式详解 观察者模式在android中的实际运用 1.观察者模式详解 2.观察者模式在...

  • Guava源码分析——EventBus

    EventBus的设计理念是基于观察者模式的,可以参考设计模式(1)—观察者模式先来了解该设计模式。 1、程序示例...

  • Android LifeCycle 源码学习总结

    整体设计 Lifecycle的核心设计模式就是观察者模式。 LifeCycleOwner 是被观察者,Lifecy...

  • PHP设计模式之观察者模式

    PHP设计模式之观察者模式

  • 设计模式 - 观察者模式

    观察者模式的定义 观察者模式简述 MVC是由各种复杂的设计模式组合而成的复合结构,观察者是其中的设计模式之一。视图...

  • 面試小記

    1:设计模式 观察者模式observer observerble,subscribe (订阅) (1)观察者对象 ...

网友评论

      本文标题:观察者设计模式

      本文链接:https://www.haomeiwen.com/subject/kfjixftx.html