iOS設計模式(01):觀察者


什麼是觀察者模式
什麼是觀察者模式?你曾經訂閱過報紙嗎?在訂閱報紙的時候,你不用去任何地方,只需要將你的個人地址信息以及訂閱信息告訴出版社,出版社就知道如何將相關報紙傳遞給你。這種模式的第二個名稱叫做發佈/訂閱模式。

 

在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
發佈了12 篇原創文章 · 獲贊 1 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章