GNUstep NSNotification(一)
2019-05-16 本文已影响0人
哦呵呵y
通知的使用主要涉及到两个方法
添加通知:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(respondsToNotification:) name:@"test" object:obj];
发送通知:
[[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:obj userInfo:@{@"key":@"value"}];
通过这两个方法就可以大致猜想到通知的具体实现方式。
- 添加通知就是将传入的参数存储到
NSNotificationCenter
单例中,其中name
以及object
两个参数可以作为存储数据的key
,而Observer
以及selector
做为value
存储 - 发送通知就是通过传入的key来查找出所有对应的value,然后使用观察者调用传入的方法。
接下来就以这两个方法为切入点分析一下具体的实现方式以及存储数据结构。
#define CHUNKSIZE 128
#define CACHESIZE 16
typedef struct NCTbl {
Observation *wildcard; /* Get ALL messages. */
GSIMapTable nameless; /* Get messages for any name. */
GSIMapTable named; /* Getting named messages only. */
unsigned lockCount; /* Count recursive operations. */
NSRecursiveLock *_lock; /* Lock out other threads. */
Observation *freeList;
Observation **chunks;
unsigned numChunks;
GSIMapTable cache[CACHESIZE];
unsigned short chunkIndex;
unsigned short cacheIndex;
} NCTable;
#define TABLE ((NCTable*)_table)
NSNotificationCenter
单例中声明了一个NCTable
的结构体指针作为所有通知的容器。
NCTable
中包含了两个MapTable
可以简单理解为字典。
这两个字典的结构一样,
-
named
存储的是添加通知时有name
参数的通知, -
nameless
存储的是没有name
参数的通知 - 所有的
Observation
对象也会保留在chunks
中,这是一个二级指针,相当于一个二维数组。如果添加通知时,没有name
以及object
就只会存在于这个指针中。
添加通知具体实现:
- (void) addObserver: (id)observer
selector: (SEL)selector
name: (NSString*)name
object: (id)object
{
Observation *list;
Observation *o;
GSIMapTable m;
GSIMapNode n;
o = obsNew(TABLE, selector, observer);
if (name)
{
...
// 添加到 named 中
}
else if (object)
{
...
// 添加到 nameless 中
}
else
{
...
// 修改wildcard指向所有 observation
}
}
- 这里就是一个简单的保存通知数据的实现
- 观察源码可以发现,添加通知时,无论有没有
name
和object
都不会引起崩溃
发送通知具体实现:
- (void) _postAndRelease: (NSNotification*)notification
{
Observation *o;
unsigned count;
NSString *name = [notification name];
id object;
GSIMapNode n;
GSIMapTable m;
GSIArrayItem i[64];
GSIArray_t b;
GSIArray a = &b;
if (name == nil)
{
RELEASE(notification);
[NSException raise: NSInvalidArgumentException
format: @"Tried to post a notification with no name."];
}
object = [notification object];
// 查找所有没有 name 也没有 object的观察者
for (o = WILDCARD = purgeCollected(WILDCARD); o != ENDOBS; o = o->next)
{
GSIArrayAddItem(a, (GSIArrayItem)o);
}
// 如果有 object, 查找
if (object)
{
n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)object);
if (n != 0)
{
o = purgeCollectedFromMapNode(NAMELESS, n);
while (o != ENDOBS)
{
GSIArrayAddItem(a, (GSIArrayItem)o);
o = o->next;
}
}
}
/*
* Find the observers of NAME, except those observers with a non-nil OBJECT
* that doesn't match the notification's OBJECT).
*/
if (name)
{
/*
* First, observers with a matching object.
*/
/*
* Now observers with a nil object.
*/
}
/*
* Now send all the notifications.
*/
count = GSIArrayCount(a);
while (count-- > 0)
{
o = GSIArrayItemAtIndex(a, count).ext;
[o->observer performSelector: o->selector
withObject: notification];
}
}
发送通知就是一个查找观察者,然后遍历发送通知的操作
- 根据源码可以看出,这里如果
name
为空会抛出异常 - 查找:
1️⃣:从wildcard
中查找所有没有name
也没有object
的观察者
2️⃣:从NAMELESS
中查找所有object
匹配的观察者
3️⃣:从NAMED
中查找所有name
匹配的观察者(内部包括object
匹配以及nil
匹配的观察者) - 遍历查找出的所有观察者对象,然后发送消息。