觀察者模式(Observer Pattern):定義對象間的一種一對多依賴關(guān)系邪媳,使得每當一個對象狀態(tài)發(fā)生改變時升酣,其相關(guān)依賴對象皆得到通知并被自動更新幻工。
在iOS中典型的觀察者模式實現(xiàn)方式為NSNotificationCenter和KVO巫延。
一效五、NSNotificationCenter
在Cocoa Touch框架中NSNotificationCenter和NSNotification對象實現(xiàn)了一對多的模型。通過NSNotificationCenter可以讓對象之間進行通訊炉峰,即便這些對象之間并不認識畏妖。NSnotificationCenter是一種典型的有調(diào)度中心的觀察者模式實現(xiàn)方式。以NSNotificationCenter為中心疼阔,觀察者往Center中注冊對某個主題對象的變化感興趣戒劫,主題對象通過NSNotificationCenter進行變化廣播。這種模型就是文章開始發(fā)布訂閱報紙在OC中的一種類似實現(xiàn)婆廊。所有的觀察和監(jiān)聽行為都向同一個中心注冊迅细,所有對象的變化也都通過同一個中心向外廣播。
NSNotificationCenter就像一個樞紐一樣淘邻,處在整個觀察者模式的核心位置茵典,調(diào)度消息在觀察者和監(jiān)聽者之間傳遞。NSNotificationCenter的底層實現(xiàn)原理請看[NSNotificationCenter實現(xiàn)原理]宾舅。(http://www.reibang.com/p/07342191866b)
1.觀察者Observer统阿,一般繼承自NSObject,通過NSNotificationCenter的addObserver:selector:name:object接口來注冊對某一類型通知感興趣.在注冊時候一定要注意筹我,NSNotificationCenter不會對觀察者進行引用計數(shù)+1的操作扶平,我們在程序中釋放觀察者的時候,一定要去報從center中將其移除了蔬蕊。
- (void)update:(NSNotification * )notification{
if ([[notification name] isEqualToString:@"subjectMessage"]) {
NSLog(@"%@",@"iOS俱哥來了");
}
}
NSNotificationCenter * notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:@selector(update:) name:@"subjectMessage" object:nil];
2.通知中心NSNotificationCenter蜻直,調(diào)度的樞紐。
3.主題對象,被觀察的對象概而,通過postNotificationName:object:userInfo:發(fā)送某一類型通知呼巷,廣播改變。
NSNotification * subjectMessage = [NSNotification notificationWithName:@"subjectMessage" object:self];
NSNotificationCenter * notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter postNotification:subjectMessage];
4.通知對象NSNotification赎瑰,當有通知來的時候王悍,Center會調(diào)用觀察者注冊的接口來廣播通知,同時傳遞存儲著更改內(nèi)容的NSNotification對象餐曼。
5.如果我們需要對監(jiān)聽進行銷毀
-(void)dealloc{
[[NSNotificationCenter defaultCenter]removeObserver:self];
}
二压储、KVO
1、實現(xiàn)機理
當某個類的對象第一次被觀察時源譬,系統(tǒng)就會在運行期動態(tài)地創(chuàng)建該類的一個派生類集惋,在這個派生類中重寫基類中任何被觀察屬性的 setter 方法。
派生類在被重寫的 setter 方法實現(xiàn)真正的通知機制踩娘,這么做是基于設(shè)置屬性會調(diào)用 setter 方法刮刑,而通過重寫就獲得了 KVO 需要的通知機制。當然前提是要通過遵循 KVO 的屬性設(shè)置方式來變更屬性值养渴,如果僅是直接修改屬性對應(yīng)的成員變量雷绢,是無法實現(xiàn) KVO 的。
同時派生類還重寫了 class 方法以“欺騙”外部調(diào)用者它就是起初的那個類理卑。然后系統(tǒng)將這個對象的 isa 指針指向這個新誕生的派生類翘紊,因此這個對象就成為該派生類的對象了,因而在該對象上對 setter 的調(diào)用就會調(diào)用重寫的 setter藐唠,從而激活鍵值通知機制帆疟。此外,派生類還重寫了 dealloc 方法來釋放資源宇立。
2.實現(xiàn)方法
KVO是一種設(shè)計模式,名為觀察者.
addObserver:forKeyPath:options:context:
通知其他對象的方法,這個方法在NSObject中就已經(jīng)申明了,也就是說任何繼承自NSObject的對象都可以使用KVO.
我們來實現(xiàn)一個對象a值改變的時候去通知對象b.
新建兩個ModelA ModelB 類.
ModelA.h
#import <Foundation/Foundation.h>
@interface ModelA : NSObject
@property(nonatomic,copy)NSString * name;
@end
ModelA.m
import "ModelA.h"
@implementation ModelA
@end
ModelB.h
#import <Foundation/Foundation.h>
@interface ModelB : NSObject
@property (nonatomic, copy) NSString *sex;
@end
ModelB.m
#import "ModelB.h"
@implementation ModelB
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
self.sex = @"female";
}
@end
然后執(zhí)行如下代碼:
- (void)viewDidLoad {
[super viewDidLoad];
ModelA * a = [[ModelA alloc]init];
ModelB * b = [[ModelB alloc]init];
//a對象要通知b對象
[a addObserver:b forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
//2秒鐘之后就改變a的name屬性
[self delay:2 execute:^{
//a對象屬相改變(發(fā)送通知消息)
a.name = @"iOS俱哥";
//打印b對象的sex屬性值
NSLog(@"%@",b.sex);
//移除a對象通知b對象
[a removeObserver:b forKeyPath:@"name"];
}];
}
- (void)delay:(int64_t)delta execute:(dispatch_block_t)block
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delta * NSEC_PER_SEC),
dispatch_get_main_queue(), block);
}
執(zhí)行結(jié)果打印了:female
如果注釋掉ModelB中的方法observeValueForKeyPath:ofObject:change:context:,運行時會導(dǎo)致崩潰:
A對象要通知B對象,B對象必須實現(xiàn)監(jiān)聽的方法,否則一旦有消息發(fā)送就會導(dǎo)致崩潰.
三鸯匹、通知中心與KVO的區(qū)別
1.發(fā)送機制的區(qū)別
通知中心自身就是中介者,兩個對象之間的通信通過中介者這個橋梁來發(fā)送信息.發(fā)送方不需要注冊任何的信息,所有的配置都由注冊方控制.
KVO是兩個對象之間直接進行通信,發(fā)送通知信息的一方(對象)的注冊鍵值發(fā)生變化的時候,會發(fā)送信息給被通知方.發(fā)送方主動添加被發(fā)送方注冊信息,被發(fā)送方還需要實現(xiàn)一個方法,兩方之間都需要進行些配置,使用稍有不當就會導(dǎo)致崩潰.
通知中心 3者間的關(guān)系
KVO 2者間的關(guān)系
2.使用的細節(jié)
KVO屬于被動發(fā)送消息,發(fā)送消息方的值改變了(一般都是被動改變的),才會發(fā)送信息給被發(fā)送方,通知中心屬于主動發(fā)送消息.
通知中心 主動
KVO 被動
3.使用難易程度
通知中心簡單暴力直白,KVO用著惡心,但惡心不代表不要用哦.
通知中心 簡單
KVO 復(fù)雜
3.使用場景
KVO監(jiān)聽僅限于屬性的變化,NSNotification是監(jiān)聽不局限于屬性的變化泄伪,還可以對多種多樣的狀態(tài)變化進行監(jiān)聽殴蓬,監(jiān)聽范圍廣,使用也更靈活蟋滴。