zoukankan      html  css  js  c++  java
  • iOS设计模式:观察者

    摘自:http://www.cocoachina.com/applenews/devnews/2013/0506/6132.html


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

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

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

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

    iOS中观察者模式的实现方法
    在iOS中观察者模式的实现有三种方法:Notification、KVO以及标准方法。
     

    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的通知

    2.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:值发生了改变标准方法

    3.标准方法

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


     

    标准方法的实现是这样的: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


    本示例的代码可以在这里下载到:https://github.com/BeyondVincent/ios_patterns/tree/master/ObserverPattern

    参考:http://maleevdimka.wordpress.com/2013/02/16/ios-patterns-observer/

    PDF下载:

    /cms/uploads/soft/130506/4196-130506101124.pdf
  • 相关阅读:
    小白们应该知道,在Eclipse当中怎样在编译页面当中显示行号.
    严重:Parse error in application web.xml file at jndi:/localhost/day_29/WEB-INF/web.xml解决办法
    linux 系统下,忘记密码的快捷解决方法。
    eclipse 当中,修改文本编辑框的字体大小
    如何在linux当中,大量的添加用户
    04文件操作1
    03python面向对象编程之多态和枚举6
    03python面向对象编程5
    03python面向对象编程4
    03python面向对象编程2
  • 原文地址:https://www.cnblogs.com/yangmx/p/3575092.html
Copyright © 2011-2022 走看看