NSNotificationCenter 內(nèi)部實現(xiàn)
通知怎么使用就不介紹了舅踪,相信大家都會使用。
參考cocotron的源代碼桥状,觀察者模式的通知一種實現(xiàn)方式如下:
主要有下列各類組成
- NSNotification:通知消息攜帶的載體雇初,通過它,可以把消息內(nèi)容傳遞給觀察者烛恤。
- NSNotificationCenter: 通知中心母怜,是一個單例,來管理觀察者和發(fā)送通知缚柏。
- NSNotificationObserver:觀察者苹熏。由注冊觀察者的類和通知selector來初始化,主要就是綁定觀察者和實現(xiàn)的通知方法selector币喧。
- NSObjectToObservers:通知調(diào)度表轨域。記錄通知name相同的一組觀察者。
- NSNotificationQueue:通知隊列杀餐。
實現(xiàn)過程:通知name和觀察者集合有NSMutableDictionary來維護key和value的對應關(guān)系干发。
- 添加觀察者,從字典中找對應的到NSObjectToObservers通知調(diào)度表史翘,然后添加觀察者枉长。
- 發(fā)送通知,找到對應的觀察者集合琼讽,然后循環(huán)通過
performSelector
方法來執(zhí)行注冊的selector必峰。 - 移除觀察者,也是找到相應的通知調(diào)度表钻蹬,然后刪除相應的觀察者吼蚁。
NSNotificationCenter 用單例來初始化
+(NSNotificationCenter *)defaultCenter {
return NSThreadSharedInstance(@"NSNotificationCenter");
}
添加觀察者
NSNotificationCenter中代碼
-(void)addObserver:anObserver selector:(SEL)selector name:(NSString *)name
object:object {
NSNotificationObserver *observer=[[[NSNotificationObserver allocWithZone:[self zone]]
initWithObserver:anObserver selector:selector] autorelease];
NSObjectToObservers *registry;
if(name==nil)//name為空時,直接對應一個觀察者集合
registry=_noNameRegistry;
else{
registry=[_nameToRegistry objectForKey:name];//取出對應的觀察者集合
if(registry==nil){
registry=[[[NSObjectToObservers allocWithZone:[self zone]] init]
autorelease];//初始化觀察者集合
[_nameToRegistry setObject:registry forKey:name];//觀察者集合和name放在字典中對應起來
}
}
[registry addObserver:observer object:object];
}
NSObjectToObservers中代碼
-(void)addObserver:(NSNotificationObserver *)observer object:objectOrNil {
id object=(objectOrNil!=nil)?objectOrNil:(id)[NSNull null];
NSMutableArray *observers=NSMapGet(_objectToObservers,object);
if(observers==nil){
observers=[NSMutableArray array];
NSMapInsert(_objectToObservers,object,observers);
}
[observers addObject:observer];
}
通知調(diào)度表是由NSMapTable的哈希表維護來記錄每一個觀察者的问欠。其中防止添加觀察者時的相同的object(發(fā)送者)肝匆,也就是hash表里的key把之前的value給覆蓋了,所以hash表里的value是一個裝有觀察者的數(shù)組集合顺献。
其中_nameToRegistry和_noNameRegistry的聲明和初始化如下:
@interface NSNotificationCenter : NSObject {
NSMutableDictionary *_nameToRegistry;
id _noNameRegistry;
}
-init {
_nameToRegistry=[[NSMutableDictionary allocWithZone:[self zone]] init];
_noNameRegistry=[[NSObjectToObservers allocWithZone:[self zone]] init];
return self;
}
NSNotificationCenter中的發(fā)送通知實現(xiàn)
static inline void postNotification(NSNotificationCenter *self,NSNotification *note){
NSAutoreleasePool *pool=[NSAutoreleasePool new];
NSObjectToObservers *registry=self->_noNameRegistry;
[registry postNotification:note];
registry=[self->_nameToRegistry objectForKey:[note name]];
[registry postNotification:note];
[pool release];
}
-(void)postNotification:(NSNotification *)note {
postNotification(self,note);
}
-(void)postNotificationName:(NSString *)name object:object userInfo:(NSDictionary *)userInfo {
NSNotification *note = NSNotification_concreteNew(NULL,name,object,userInfo);
postNotification(self,note);
[note release];
}
-(void)postNotificationName:(NSString *)name object:object {
NSNotification *note = NSNotification_concreteNew(NULL,name,object,nil);
postNotification(self,note);
[note release];
}
作為通知中心來說旗国,只是告知哪一個觀察者集合需要發(fā)送哪一個name的通知,具體執(zhí)行還是得靠通知調(diào)度表分發(fā)下去注整。
-(void)postNotification:(NSNotification *)note {
// FIXME: NSNotificationCenter sends notifications in the order they are added for observation regardless of
// the object registered. This implementation stores objects for observation seperately so if you observe nil
// and a particular object you will always get the particular object notifications before the nil one instead
// of in the order they are registered.
// The copy and double check for presence is to deal with observers being removed during notification
id object=[note object];
NSArray *observers;
NSInteger count;
if(object!=nil){
observers=[NSArray arrayWithArray:(id)NSMapGet(_objectToObservers,object)];
count=[observers count];
while(--count>=0){
id observer=[observers objectAtIndex:count];
if([(NSArray *)NSMapGet(_objectToObservers,object) indexOfObjectIdenticalTo:observer]!=NSNotFound)
[observer performSelector:_cmd withObject:note];
}
}
observers=[NSArray arrayWithArray:(id)NSMapGet(_objectToObservers,[NSNull null])];
count=[observers count];
while(--count>=0){
id observer=[observers objectAtIndex:count];
if([(NSArray *)NSMapGet(_objectToObservers,[NSNull null]) indexOfObjectIdenticalTo:observer]!=NSNotFound)
[observer performSelector:_cmd withObject:note];
}
}
觀察者執(zhí)行通知
-(void)postNotification:(NSNotification *)note {
[_observer performSelector:_selector withObject:note];
}
移除觀察者
在對象被釋放前需要移除掉觀察者粗仓,避免已經(jīng)被釋放的對象還接收到通知導致崩潰嫁怀。
NSNotificationCenter中代碼
-(void)removeObserver:anObserver {
NSMutableArray *removeRegistries=[NSMutableArray array];
NSEnumerator *keyEnumerator=[_nameToRegistry keyEnumerator];
NSString *key;
NSObjectToObservers *registry;
NSInteger count;
while((key=[keyEnumerator nextObject])!=nil){
registry=[_nameToRegistry objectForKey:key];
[registry removeObserver:anObserver object:nil];
if([registry count]==0)
[removeRegistries addObject:key];
}
[_noNameRegistry removeObserver:anObserver object:nil];
count=[removeRegistries count];
while(--count>=0)
[_nameToRegistry removeObjectForKey:[removeRegistries objectAtIndex:count]];
}
-(void)removeObserver:anObserver name:(NSString *)name object:object {
NSMutableArray *removeRegistries=[NSMutableArray array];
NSObjectToObservers *registry;
NSInteger count;
if(name!=nil){
registry=[_nameToRegistry objectForKey:name];
[registry removeObserver:anObserver object:object];
if([registry count]==0)
[removeRegistries addObject:name];
}
else {
NSEnumerator *keyEnumerator=[_nameToRegistry keyEnumerator];
NSString *key;
while((key=[keyEnumerator nextObject])!=nil){
registry=[_nameToRegistry objectForKey:key];
[registry removeObserver:anObserver object:object];
if([registry count]==0)
[removeRegistries addObject:key];
}
[_noNameRegistry removeObserver:anObserver object:object];
}
count=[removeRegistries count];
while(--count>=0){
NSString *key=[removeRegistries objectAtIndex:count];
NSObjectToObservers *remove=[_nameToRegistry objectForKey:key];
[[remove retain] autorelease];
[remove invalidate];
[_nameToRegistry removeObjectForKey:key];
}
}
NSObjectToObservers實現(xiàn)如下:
-(void)removeObserver:anObserver object:object {
id removeKeys[NSCountMapTable(_objectToObservers)];
int removeCount=0;
if(object!=nil){
NSMutableArray *observers=NSMapGet(_objectToObservers,object);
NSInteger count=[observers count];
while(--count>=0)
if(anObserver==[[observers objectAtIndex:count] observer])
[observers removeObjectAtIndex:count];
if([observers count]==0)
removeKeys[removeCount++]=object;
}
else {
NSMapEnumerator state=NSEnumerateMapTable(_objectToObservers);
id key;
NSMutableArray *observers;
while(NSNextMapEnumeratorPair(&state,(void **)&key,(void **)&observers)){
NSInteger count=[observers count];
while(--count>=0)
if(anObserver==[[observers objectAtIndex:count] observer])
[observers removeObjectAtIndex:count];
if([observers count]==0)
removeKeys[removeCount++]=object;
}
}
while(--removeCount>=0)
NSMapRemove(_objectToObservers,removeKeys[removeCount]);
}
從hash表中刪除相應的觀察者。
通知中心用到的概念還有NSNotification
和NSNotificationQueue
等借浊。
疑問:若是一個類中注冊了多個觀察者塘淑,則在通知調(diào)度表中會記錄幾次呢?
結(jié)合通知實現(xiàn)和NSMapTable的hash規(guī)則可以看到,每一通知調(diào)度表維護一張hash表蚂斤,每一個hash表示根據(jù)key(通知的object)來插入裝有value(觀察者)的數(shù)組存捺。所以觀察者的存儲次數(shù)會根據(jù)通知名字和keyobject)來發(fā)送通知而定,有可能記錄多次曙蒸。