一破托、觀察者模式和發(fā)布訂閱模式簡介
1.1 觀察者模式
觀察者模式定義了一種一對多的依賴關(guān)系,讓多個(gè)觀察者對象同時(shí)監(jiān)聽某一個(gè)主題對象歧蒋。這個(gè)主題對象在狀態(tài)上發(fā)生變化時(shí)土砂,會通知所有觀察者對象,使它們能夠自動更新自己疏尿。
1.2 發(fā)布訂閱模式
發(fā)布訂閱模式理念和觀察者模式相同瘟芝,但是處理方式上不同:訂閱者把自己想訂閱的事件注冊到調(diào)度中心易桃,當(dāng)該事件觸發(fā)時(shí)候褥琐,發(fā)布者發(fā)布該事件到調(diào)度中心(順帶上下文),由調(diào)度中心統(tǒng)一調(diào)度訂閱者注冊到調(diào)度中心的處理代碼晤郑。
1.3 兩者之間的差異
1敌呈、在觀察者模式中贸宏,觀察者是知道Subject的,Subject一直保持對觀察者進(jìn)行記錄磕洪。然而吭练,在發(fā)布訂閱模式中,發(fā)布者和訂閱者不知道對方的存在析显。它們只有通過消息代理進(jìn)行通信鲫咽。
2、發(fā)布訂閱模式中谷异,組件是松散耦合的分尸,正好和觀察者模式相反。
二歹嘹、觀察者模式和發(fā)布訂閱模式優(yōu)缺點(diǎn)
2.1 優(yōu)點(diǎn)
共同有點(diǎn):
1箩绍、都可以一對多
2、程序便于擴(kuò)展
觀察者模式:
單向解耦尺上,發(fā)布者不需要清楚訂閱者何時(shí)何地訂閱材蛛,只需要維護(hù)訂閱隊(duì)列,發(fā)送消息即可
發(fā)布訂閱模式:
雙向解耦怎抛,發(fā)布者和訂閱者都不用清楚對方卑吭,全部由訂閱中心做處理
2.2 缺點(diǎn)
1、如果一個(gè)被觀察者對象有很多的直接和間接的觀察者的話抽诉,將所有的觀察者都通知到會花費(fèi)很多時(shí)間陨簇。
2、如果在觀察者和觀察目標(biāo)之間有循環(huán)依賴的話迹淌,觀察目標(biāo)會觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用河绽,可能導(dǎo)致系統(tǒng)崩潰。 3唉窃、沒有相應(yīng)的機(jī)制讓觀察者知道所觀察的目標(biāo)對象是怎么發(fā)生變化的耙饰,而僅僅只是知道觀察目標(biāo)發(fā)生了變化。
4纹份、發(fā)布者不知道訂閱者是否收到發(fā)布的消息苟跪。
5、訂閱者不知道自己是否收到了發(fā)布者發(fā)出的所有消息蔓涧。
6件已、發(fā)送者不能獲知訂閱者的執(zhí)行情況。
7元暴、沒人知道訂閱者何時(shí)開始收到消息篷扩。
8、發(fā)布訂閱模式茉盏,中心任務(wù)過重鉴未,一旦崩潰枢冤,所有訂閱者都會受到影響。
三铜秆、應(yīng)用場景
1淹真、一個(gè)抽象模型有兩個(gè)方面,其中一個(gè)方面依賴于另一個(gè)方面连茧。將這些方面封裝在獨(dú)立的對象中使它們可以各自獨(dú)立地改變和復(fù)用核蘸。
2、一個(gè)對象的改變將導(dǎo)致其他一個(gè)或多個(gè)對象也發(fā)生改變啸驯,而不知道具體有多少對象將發(fā)生改變值纱,可以降低對象之間的耦合度。
3坯汤、一個(gè)對象必須通知其他對象虐唠,而并不知道這些對象是誰。
4惰聂、需要在系統(tǒng)中創(chuàng)建一個(gè)觸發(fā)鏈疆偿,A對象的行為將影響B(tài)對象,B對象的行為將影響C對象……搓幌,可以使用觀察者/訂閱發(fā)布創(chuàng)建一種鏈?zhǔn)接|發(fā)機(jī)制杆故。
四、實(shí)現(xiàn)
4.1 觀察者模式
目標(biāo):
// 將觀察者添加到數(shù)組中
- (void)addObserver:(NSObject *)obj selector:(SEL)aSelector {
NSDictionary *dict = @{@"object":obj,@"sel":NSStringFromSelector(aSelector)};
[self.observers addObject:dict];
}
//發(fā)送通知給觀察者
- (void)notify {
if (_observers) {
for (NSDictionary *dict in _observers) {
if ([dict isKindOfClass:[NSDictionary class]]) {
NSObject *observer = dict[@"object"];
SEL aSelector = NSSelectorFromString(dict[@"sel"]);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[observer performSelector:aSelector];
#pragma clang diagnostic pop
}
}
}
}
//創(chuàng)建觀察者數(shù)組
- (NSMutableArray *)observers {
if (!_observers) {
_observers = [NSMutableArray array];
}
return _observers;
}
觀察者:
// 接收到消息通知
- (void)update {
NSLog(@"我觀察的目標(biāo)發(fā)生了變化溉愁,我接收到了新的信息,%@",self.name);
}
4.2 發(fā)布訂閱模式
目標(biāo):
// 發(fā)送消息給通知中心
- (void)change {
[[NotifyCenter shared] notify:@"change"];
}
通知中心:
//創(chuàng)建通知中心單例
+ (NotifyCenter *)shared {
static NotifyCenter *shared;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shared = [[self alloc] init];
});
return shared;
}
// 添加觀察者
- (void)addObserver:(NSObject *)object selector:(SEL)aSelector name:(NSString *)aName {
[self.set addObject:aName];
NSDictionary *dict = @{@"object":object,@"sel":NSStringFromSelector(aSelector)};
NSMutableDictionary *observer = [NSMutableDictionary dictionary];
observer[aName] = dict;
[self.observers addObject:observer];
}
// 通知觀察者
- (void)notify:(NSString *)name {
if ([_set containsObject:name]) {
[_observers enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSDictionary *dict = obj[name];
if ([dict isKindOfClass:[NSDictionary class]]) {
NSObject *observer = dict[@"object"];
SEL aSelector = NSSelectorFromString(dict[@"sel"]);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[observer performSelector:aSelector];
#pragma clang diagnostic pop
}
}];
}
}
觀察者:
// 接收到消息通知
- (void)update {
NSLog(@"我觀察的目標(biāo)發(fā)生了變化处铛,我接收到了新的信息,%@",self.name);
}
4.4 調(diào)用
- (void)viewDidLoad {
[super viewDidLoad];
//觀察者模式
//創(chuàng)建目標(biāo)
self.subject = [[Subject alloc] init];
//創(chuàng)建觀察者1
Observer *one = [[Observer alloc] init];
one.name = @"one";
//向目標(biāo)訂閱通知
[_subject addObserver:one selector:@selector(update)];
//創(chuàng)建觀察者2
Observer *two = [[Observer alloc] init];
two.name = @"two";
//向目標(biāo)訂閱通知
[_subject addObserver:two selector:@selector(update)];
//發(fā)布訂閱模式
//創(chuàng)建目標(biāo)
self.subscribe = [[Subscribe alloc] init];
//創(chuàng)建觀察者3
Observer *third = [[Observer alloc] init];
third.name = @"third";
//觀察者向通知中心訂閱消息
[[NotifyCenter shared] addObserver:third selector:@selector(update) name:@"change"];
//創(chuàng)建觀察者4
Observer *four = [[Observer alloc] init];
four.name = @"four";
//觀察者向通知中心訂閱消息
[[NotifyCenter shared] addObserver:four selector:@selector(update) name:@"change"];
//按鈕觸發(fā)目標(biāo)發(fā)送消息
UIButton *testBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 100, 100, 40)];
testBtn.backgroundColor = [UIColor lightGrayColor];
[testBtn addTarget:self action:@selector(testButton) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:testBtn];
}
- (void)testButton {
NSLog(@"差不多11:45分了,到點(diǎn)吃飯了");
NSLog(@"觀察者模式發(fā)送消息");
[_subject notify];
NSLog(@"發(fā)布-訂閱模式發(fā)送消息");
[_subscribe change];
}
五拐揭、總結(jié)
1撤蟆、雖然兩種模式都存在訂閱者和發(fā)布者(具體觀察者可認(rèn)為是訂閱者、具體目標(biāo)可認(rèn)為是發(fā)布者)堂污,但是觀察者模式是由具體目標(biāo)調(diào)度的家肯,而發(fā)布/訂閱模式是統(tǒng)一由調(diào)度中心調(diào)的,所以觀察者模式的訂閱者與發(fā)布者之間是存在依賴的盟猖,而發(fā)布/訂閱模式則不會讨衣。
2、兩種模式都可以用于松散耦合式镐,改進(jìn)代碼管理和潛在的復(fù)用反镇。