[iOS]观察者模式

来源:互联网 发布:有卖淘宝店铺的吗 编辑:程序博客网 时间:2024/06/07 23:53

iOS中典型的观察者模式为NSNotificationCenter和KVO

一、Notification

观察者向通知中心注册,声明对某个对象的变化感兴趣。该对象通过NSNotificationCenter进行广播。观察者就会收到通知中心的通知,采取相应的操作。


1.观察者Observer,一般继承自NSObject,通过NSNotificationCenter的addObserver:selector:name:object接口来注册对某一类型通知感兴趣.在注册时候一定要注意,NSNotificationCenter不会对观察者进行引用计数+1的操作,我们在程序中释放观察者的时候,一定要去报从center中将其注销了。

- (void)handleMessage:(NSNotification*)nc

{
     //解析消息内容
NSDictionary* userInfo = [nc userInfo];
}

该方法是回调函数,用来处理接到消息以后的动作。有一个NSNotification类的参数,该类有三个重要属性:name、object和useInfo。

其中name是通知的名字,object是投送通知时传递过来的对象,userInfo是投送通知时定义的字典对象,可以借助改参数传递数据。


-(void)commonInit
 {
     //注册观察者
     [[NSNotificationCenterdefaultCenter] addObserver:self 

selector:@selector(handleMessage:) 

    name:kDZTestNotificatonMessage 

    object:nil];
 }


2、通知中心NSNotificationCenter,通知的枢纽。
3、主题对象,被观察的对象,通过postNotificationName:object:userInfo:发送某一类型通知,广播改变。

-(void)postMessage
 {
 [[NSNotificationCenter defaultCenter] postNotificationName:kDZTestNotificatonMessage 

      object:Nil 

  userInfo:@{}];

 }


4、通知对象NSNotification,当有通知来的时候,Center会调用观察者注册的接口来广播通知,同时传递存储着更改内容的NSNotification对象。

缺点:
1、每个注册的地方需要同时注册一个函数,这将会带来大量的编码工作。仔细分析能够发现,其实我们每个观察者每次注册的函数几乎都是雷同的。这就是种变相的CtrlCV,是典型的丑陋和难维护的代码。
2、每个观察者的回调函数,都需要对主题对象发送来的消息进行解包的操作。从UserInfo中通过KeyValue的方式,将消息解析出来,而后进行操作。试想一下,工程中有100个地方,同时对前面中在响应变化的函数中进行了解包的操作。而后期需求变化需要多传一个内容的时候,将会是一场维护上的灾难。
3、当大规模使用观察者模式的时候,我们往往在dealloc处加上一句:
[[NSNotificationCenter defaultCenter] removeObserver:self] , 而在实际使用过程中,会发现该函数的性能是比较低下的。


二、KVO模式

key-value observing

在MVC中,当model对象发生改变时,view应该随之改变,以反映模型的变化;当用户和controller交互的时候,model也应该做出相应的改变。KVO可以帮助让view和model保持同步。controller可以通过观察试图依赖的属性,做出改变。

一开始注册一个监听者,声明一个被观察的对象以及监听的属性,当该对象被改变时,KVO就会自动发通知给监听者,而不是通过通知中心。然后监听者会执行回调函数。

1、注册监听者的函数:

[A addObserver: observer forKeyPath: @"frame" options: 0 context: nil];

A : 监听者

observer:被观察的对象

forKeyPatch:被监听的属性

option:有4 个可选的值:

NSKeyValueObservingOptionNew 把更改之前的值提供给处理方法

NSKeyValueObservingOptionOld 把更改之后的值提供给处理方法

NSKeyValueObservingOptionInitial 把初始化的值提供给处理方法,一旦注册,立马调用一次。通常它会带有新值,而不会带旧值

NSKeyValueObservingOptionPrior 分2次调用。在值改变之前和之后。

0:不带任何参数进去

context:可以带入一些参数。任何类型都可以,可以强转


解除注册的函数:

[A removeObserver: (NSObject * ) observer forKeyPath: (NSString * ) keyPath ];

注意:一定要手动解除注册,不然会导致泄露

NSObject,NSArray,NSSet均实现了以上方法,因此我们不仅可以观察普通对象,还可以观察数组或结合类对象。


2、设置属性

只有遵循 KVO 方式来设置属性,观察者对象才会获取通知,也就是说遵循使用属性的 setter 方法,或通过 key-path 来设置:

[target setAge:30]; 
[target setValue:[NSNumber numberWithInt:30] forKey:@"age"];

[target setValue:@(20) forKey:@"age"];

KVC 允许我们用属性的字符串名称来访问属性,字符串在这儿叫做键


3、处理变更通知
观察者需要实现名为 NSKeyValueObserving 的 category 方法来处理收到的变更通知:

- (void)observeValueForKeyPath: (NSString *)keyPath 

      ofObject:( id)object 

change: (NSDictionary *)change 

context: (void *)context;

在这里,change 这个字典保存了变更信息,具体是哪些信息取决于注册时的 NSKeyValueObservingOptions。

可以用以下方式提取出改变前后的值:

id oldValue = change[NSKeyValueChangeOldKey];
id newValue = change[NSKeyValueChangeNewKey];


KVO 旨在观察关系 (relationship) 而不是集合。我们不能观察 NSArray,我们只能观察一个对象的属性——而这个属性有可能是 NSArray。举例说,如果我们有一个 ContactList 对象,我们可以观察它的 contacts 属性。但是我们不能向要观察对象的 -addObserver:forKeyPath:... 传入一个 NSArray。

相似地,观察 self 不是永远都生效的。而且这不是一个好的设计。


你可以在 lldb 里查看一个被观察对象的所有观察信息。
(lldb) po [observedObject observationInfo]


稍等填坑

0 0