NSNotificationCenter 内部实现
通知怎么使用就不介绍了,相信大家都会使用。
参考cocotron的源代码,观察者模式的通知一种实现方式如下:
主要有下列各类组成
- NSNotification:通知消息携带的载体,通过它,可以把消息内容传递给观察者。
- NSNotificationCenter: 通知中心,是一个单例,来管理观察者和发送通知。
- NSNotificationObserver:观察者。由注册观察者的类和通知selector来初始化,主要就是绑定观察者和实现的通知方法selector。
- NSObjectToObservers:通知调度表。记录通知name相同的一组观察者。
- NSNotificationQueue:通知队列。
实现过程:通知name和观察者集合有NSMutableDictionary来维护key和value的对应关系。
- 添加观察者,从字典中找对应的到NSObjectToObservers通知调度表,然后添加观察者。
- 发送通知,找到对应的观察者集合,然后循环通过
performSelector
方法来执行注册的selector。 - 移除观察者,也是找到相应的通知调度表,然后删除相应的观察者。
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];
}
通知调度表是由NSMapTable的哈希表维护来记录每一个观察者的。其中防止添加观察者时的相同的object(发送者),也就是hash表里的key把之前的value给覆盖了,所以hash表里的value是一个装有观察者的数组集合。
其中_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中的发送通知实现
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];
}
作为通知中心来说,只是告知哪一个观察者集合需要发送哪一个name的通知,具体执行还是得靠通知调度表分发下去。
-(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];
}
}
观察者执行通知
-(void)postNotification:(NSNotification *)note {
[_observer performSelector:_selector withObject:note];
}
移除观察者
在对象被释放前需要移除掉观察者,避免已经被释放的对象还接收到通知导致崩溃。
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实现如下:
-(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
等。
疑问:若是一个类中注册了多个观察者,则在通知调度表中会记录几次呢?
结合通知实现和NSMapTable的hash规则可以看到,每一通知调度表维护一张hash表,每一个hash表示根据key(通知的object)来插入装有value(观察者)的数组。所以观察者的存储次数会根据通知名字和keyobject)来发送通知而定,有可能记录多次。