源码解析

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"}];

通过这两个方法就可以大致猜想到通知的具体实现方式。

  1. 添加通知就是将传入的参数存储到NSNotificationCenter单例中,其中name 以及 object两个参数可以作为存储数据的key,而Observer以及selector做为value存储
  2. 发送通知就是通过传入的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可以简单理解为字典。

image.png
这两个字典的结构一样,
  1. named 存储的是添加通知时有name参数的通知,
  2. nameless 存储的是没有name参数的通知
  3. 所有的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
    }
}
  1. 这里就是一个简单的保存通知数据的实现
  2. 观察源码可以发现,添加通知时,无论有没有nameobject都不会引起崩溃
    发送通知具体实现:
- (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];
    }
}

发送通知就是一个查找观察者,然后遍历发送通知的操作

  1. 根据源码可以看出,这里如果name为空会抛出异常
  2. 查找:
    1️⃣:从wildcard中查找所有没有name也没有 object的观察者
    2️⃣:从NAMELESS 中查找所有object匹配的观察者
    3️⃣:从NAMED中查找所有name匹配的观察者(内部包括object匹配以及nil匹配的观察者)
  3. 遍历查找出的所有观察者对象,然后发送消息。
上一篇下一篇

猜你喜欢

热点阅读