iOS设计模式(01):观察者

本文详细介绍了观察者模式的概念及其在iOS开发中的应用。通过三种不同的实现方式——NSNotification、KVO和标准方法,展示了如何在iOS应用程序中实现对象间的解耦通信。


什么是观察者模式

什么是观察者模式?你曾经订阅过报纸吗?在订阅报纸的时候,你不用去任何地方,只需要将你的个人地址信息以及订阅信息告诉出版社,出版社就知道如何将相关报纸传递给你。这种模式的第二个名称叫做发布/订阅模式

在GoF中是这样描述观察者模式的——观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

观察者模式的的思想非常简单,Subject(主题)允许别的对象——观察者(这些对象实现了观察者接口)对这个Subject的改变进行订阅和取消订阅。当Subject发生了变化——那么Subject会将这个变化发送给所有的观察者,观察者就能对Subject的变化做出更新。在这里,Subject是报纸的出版社,而观察者则是订阅报纸的我和你,当Subject发生变化——有新的报纸,会做出通知——将报纸发送给所有的订阅者。

什么时候使用观察者模式?

  1. 当你需要将改变通知所有的对象时,而你又不知道这些对象的具体类型,此时就可以使用观察者模式。
  2. 改变发生在同一个对象中,并在别的地方需要将相关的状态进行更新。

iOS中观察者模式的实现方法

在iOS中观察者模式的实现有三种方法:

  1. NOTIFICATION

Notification - NotificationCenter机制使用了操作系统的功能。通过NSNotificationCenter可以让对象之间进行进行通讯,这些对象相互间可以不认识。当你用一个并行的流来推送通知,或者刷新数据库,并希望在界面中能够看到时,这非常有用。

NotificationCenter发布消息的方法如下所示:

 
  1. NSNotification * broadcastMessage = [ NSNotification notificationWithName: AnyNotification object: Self ];
  2. NSNotificationCenter * notificationCenter = [ NSNotificationCenter defaultCenter];
  3. [NotificationCenter postNotification: broadCastMessage];

上面的代码中,创建了一个NSNotification类型的对象,并指定名称为”broadcastMessage”,然后通过notificationCenter来发布这个消息。

要订阅感兴趣的对象中的相关事件,可以按照如下方法进行:

 
  1. NSNotificationCenter * notificationCenter = [ NSNotificationCenter defaultCenter];
  2. [NotificationCenter addObserver: Self selector: @ selector (update:) name: AnyNotification object: nil ];

如上代码所示:订阅了一个事件,并通过@selector指定了一个方法。

 
  1. // 收到通知中心发来的通知
  2. -(void)update:(NSNotification *) notification
  3. {
  4. if ([[notification name] isEqualToString:AnyNotification])
  5. NSLog (@"成功收到通知中心发来的名为%@的通知", AnyNotification);
  6. }

下面是运行上面代码,在控制台输出的内容:

 
  1. 2013-05-05 23:43:15.570 ObserverPattern[1738:c07] 成功收到通知中心发来的名为broadcastMessage的通知
  1.  KVO

通过KVO,某个对象中的特定属性发生了改变,别的对象可以获得通知。苹果官方文档对KVO有了很好的解释:Key-Value Observing Programming Guide。下面两种方法都可以改变对象中属性的值:

 
  1. kvoSubj.changeableProperty = @"新的一个值";
  2.  
  3. [kvoSubj setValue:@"新的一个值" forKey:@"changeableProperty"];

上面这种值改变的灵活性可以让我们对键值进行观察。

下面是新建的一个类KVOSubject,这个类中有一个属性changeableProperty:

 
  1. @interface KVOSubject : NSObject
  2.  
  3. @property (nonatomic, strong) NSString *changeableProperty;
  4.  
  5. @end
  6.  
  7. @implementation KVOSubject
  8.  
  9. @end

接着新建了另外一个类KVOObserver,通过该类可以监听changeableProperty属性值的改变。

 
  1. @interface KVOObserver : NSObject
  2. @end
  3.  
  4. @implementation KVOObserver
  5.  
  6. -(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
  7. {
  8. NSLog(@"KVO:值发生了改变");
  9. }
  10.  
  11. @end

如上代码所示,KVOObserver类只有一个方法observeValueForKeyPath。当changeableProperty属性值的改变时,这个方法会被调用。下面是测试的代码:

 
  1. - (IBAction)btnKVOObservationTest:(id)sender {
  2. KVOSubject *kvoSubj = [[KVOSubject alloc] init];
  3. KVOObserver *kvoObserver = [[KVOObserver alloc] init];
  4.  
  5. [kvoSubj addObserver:kvoObserver forKeyPath:@"changeableProperty"
  6. options:NSKeyValueObservingOptionNew context:nil];
  7.  
  8. kvoSubj.changeableProperty = @"新的一个值";
  9.  
  10. [kvoSubj setValue:@"新的一个值" forKey:@"changeableProperty"];
  11.  
  12. [kvoSubj removeObserver:kvoObserver forKeyPath:@"changeableProperty"];
  13. }

执行上面的代码,可以看到控制台输出如下结果:

 
  1. 2013-05-05 23:10:20.789 ObserverPattern[1358:c07] KVO:值发生了改变
  2. 2013-05-05 23:10:20.790 ObserverPattern[1358:c07] KVO:值发生了改变
  1. 标准方法

先来看看Gof中对观察者模式定义的结构图:

Observer-Structure

标准方法的实现是这样的:Subject(主题)知道所有的观察者,但是不知道它们的类型。下面我们就从创建Subject和Observer(观察者)的协议(protocol)开始。

 
  1. @protocol StandardObserver
  2. -(void) valueChanged:(NSString *)valueName newValue:(NSString *) newValue;
  3. @end
  4.  
  5. @protocol StandardSubject
  6. -(void) addObserver:(id) observer;
  7. -(void) removeObserver:(id) observer;
  8. -(void) notifyObjects;
  9. @end

下面,我们来创建一个Subject的implementation (实现)

 
  1. @interface StandardSubjectImplementation : NSObject
  2. {
  3. @private NSString *_valueName;
  4. @private NSString *_newValue;
  5. }
  6. @property (nonatomic, strong) NSMutableSet *observerCollection;
  7. -(void)changeValue:(NSString *)valueName andValue:(NSString *) newValue;
  8. @end
  9.  
  10. @implementation StandardSubjectImplementation
  11.  
  12. -(NSMutableSet *) observerCollection
  13. {
  14. if (_observerCollection == nil)
  15. _observerCollection = [[NSMutableSet alloc] init];
  16.  
  17. return _observerCollection;
  18. }
  19.  
  20. -(void) addObserver:(id)observer
  21. {
  22. [self.observerCollection addObject:observer];
  23. }
  24.  
  25. -(void) removeObserver:(id)observer
  26. {
  27. [self.observerCollection removeObject:observer];
  28. }
  29.  
  30. -(void) notifyObjects
  31. {
  32. for (id observer in self.observerCollection) {
  33. [observer valueChanged: _valueName newValue:_newValue];
  34. }
  35. }
  36.  
  37. -(void)changeValue:(NSString *)valueName andValue:(NSString *) newValue
  38. {
  39. _newValue = newValue;
  40. _valueName = valueName;
  41. [self notifyObjects];
  42. }
  43. @end

接下来是Observer的implementation (实现):

 
  1. @interface SomeSubscriber : NSObject
  2. @end
  3.  
  4. @implementation SomeSubscriber
  5. -(void) valueChanged:(NSString *)valueName newValue:(NSString *)newValue
  6. {
  7. NSLog(@"SomeSubscriber输出: 值 %@ 已变为 %@", valueName, newValue);
  8. }
  9. @end
  10.  
  11. @interface OtherSubscriber : NSObject
  12.  
  13. @end
  14.  
  15. @implementation OtherSubscriber
  16.  
  17. -(void) valueChanged:(NSString *)valueName newValue:(NSString *)newValue
  18. {
  19. NSLog(@"OtherSubscriber输出: 值 %@ 已变为 %@", valueName, newValue);
  20. }
  21. @end

下面是演示的代码:

 
  1. StandardSubjectImplementation * subj = [[StandardSubjectImplementation alloc] init];
  2. SomeSubscriber * someSubscriber = [[SomeSubscriber alloc] init];
  3. OtherSubscriber * otherSubscriber = [[OtherSubscriber alloc] init];
  4.  
  5. [Subj addObserver: someSubscriber];
  6. [Subj addObserver: otherSubscriber];
  7.  
  8. [subj changeValue:@"version" andValue:@"1.0.0"];

上面代码运行的log如下所示:

 
  1. 2013-05-05 23:19:04.662 ObserverPattern[1459:c07] OtherSubscriber输出: version 已变为 1.0.0
  2. 2013-05-05 23:19:04.664 ObserverPattern[1459:c07] SomeSubscriber输出: version 已变为 1.0.0

代码实例

本文涉及到的相关实例代码和PDF归档可以到点击下图下载:

代码下载            pdf-icon

 

美文推荐:When to use Delegation, Notification, or Observation in iOS

_________________________________________

本文由破船原创
转载请注明出处:BeyondVincent的博客
_________________________________________

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值