GNUstep NSNotification(一)

通知的使用主要涉及到两个方法
添加通知:

[[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. 遍历查找出的所有观察者对象,然后发送消息。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,110评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,443评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,474评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,881评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,902评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,698评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,418评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,332评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,796评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,968评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,110评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,792评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,455评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,003评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,130评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,348评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,047评论 2 355

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,103评论 1 32
  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 5,320评论 0 9
  • 点击查看原文 Web SDK 开发手册 SDK 概述 网易云信 SDK 为 Web 应用提供一个完善的 IM 系统...
    layjoy阅读 13,768评论 0 15
  • 转载自南峰子的技术博客 一个NSNotificationCenter对象(通知中心)提供了在程序中广播消息的机制,...
    我消失1314阅读 890评论 0 2
  • 看到一句话,如果有一天我们都老去,突然心生感慨。是啊,有一天我们真的都会老去,当满头白发,步伐蹒跚在夕阳西下的时候...
    雨天的那棵树阅读 2,022评论 4 21