iOS—NSNotification实现

iOS—NSNotification实现,第1张

文章目录 NSNotificationCenter:消息中心结构named,有传入NotificationName的表nameless,没有传入NotificationName的表wildcard,没有传入NotificationName和object的表 Observation:保存了观察者信息工作流程,GNUStep实现添加观察者发送通知移除通知 一些问题通知中心发送通知给观察者是同步的怎样异步?在通知时间处理方法中,开启子线程,将 *** 作放进子线程中使用通知队列 (NSNotificationQueue)自己在子线程中用通知队列没效果,又是和runloop有关.. 页面销毁时不移除通知会崩溃吗多次添加同一个通知会是什么结果?多次移除通知?下面的方法不会接收到通知?

通知机制的核心是一个与线程关联的单例对象,通知中心(NSNotificationCenter)。通知中心同步发送通知给观察者,也可以使用通知队列(NSNotificationQueue)异步发送通知。

基于GNUStep源码

NSNotificationCenter:消息中心

获取单例对象center的类方法

+ (NSNotificationCenter*) defaultCenter
{
  return default_center;
}
结构
typedef struct NCTbl {
  Observation		*wildcard;	/* Get ALL messages.		*/
  GSIMapTable		nameless;	/* Get messages for any name.	*/
  GSIMapTable		named;		/* Getting named messages only.	*/
	...
} NCTable;
wildcard,是链表的数据结构,如果在注册观察者时即没有传入NotificationName,也没有传入object,就会添加到wildcard链表中。注册到这里的观察者能接收到 所有的系统通知。nameless,注册观察者时没有传入NotificationName的表named,注册观察者时传入了NotificationName的表 named,有传入NotificationName的表
 [[NSNotificationCenter defaultCenter] addObserver:observer selector:@selector(test) name:notificationName object:object];

named表

key为通知名NotificationNamevalue又是一张表,这张表中 key:发送通知的对象objectvalue:观察者observer,链表形式,保存多个观察者

学姐博客里的图

nameless,没有传入NotificationName的表

wildcard,没有传入NotificationName和object的表

就是个链表。
注册到这里的观察者能接收到所有的系统通知。

Observation:保存了观察者信息
typedef	struct	Obs {
  id		observer;	/* Object to receive message.	*/
  SEL		selector;	/* Method selector.		*/
  struct Obs	*next;		/* Next item in linked list.	*/
  int		retained;	/* Retain count for structure.	*/
  struct NCTbl	*link;		/* Pointer back to chunk table	*/
} Observation;

工作流程,GNUStep实现 添加观察者
- (void) addObserver: (id)observer
	    selector: (SEL)selector
                name: (NSString*)name
	      object: (id)object
{
  Observation	*list;
  Observation	*o;
  GSIMapTable	m;
  GSIMapNode	n;
    //observer观察者为nil,出错
  if (observer == nil)
    [NSException raise: NSInvalidArgumentException
		format: @"Nil observer passed to addObserver ..."];
    
    //selector为空,报错
  if (selector == 0)
    [NSException raise: NSInvalidArgumentException
		format: @"Null selector passed to addObserver ..."];
    //观察者不能响应selector方法时报错
  if ([observer respondsToSelector: selector] == NO)
    {
      [NSException raise: NSInvalidArgumentException
        format: @"[%@-%@] Observer '%@' does not respond to selector '%@'",
        NSStringFromClass([self class]), NSStringFromSelector(_cmd),
        observer, NSStringFromSelector(selector)];
    }
    //给表上锁
  lockNCTable(TABLE);
    //建立新的Observation
  o = obsNew(TABLE, selector, observer);

    //如果有通知名
  if (name) {

        //在named表中,以name为key找到对应的value
      n = GSIMapNodeForKey(NAMED, (GSIMapKey)(id)name);
        //named中没有key对应的value
      if (n == 0) {
          //新建一个表
          m = mapNew(TABLE);
        
          //由于这是对给定名称的首次观察,因此我们对该名称进行了复制,以便在map中无法对其进行更改
          name = [name copyWithZone: NSDefaultMallocZone()];
          //新建的表作为name(key)对应的的value 添加在named表中
          GSIMapAddPair(NAMED, (GSIMapKey)(id)name, (GSIMapVal)(void*)m);
          GS_CONSUMED(name)
      } else {
          //取出对应的value
          m = (GSIMapTable)n->value.ptr;
      }
        
        //从上面取出的value中,取出object(key)对应的链表
      n = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
        
        //将observation添加到对应的链表中
        if (n == 0) {
          o->next = ENDOBS;
          GSIMapAddPair(m, (GSIMapKey)object, (GSIMapVal)o);
        } else {
          list = (Observation*)n->value.ptr;
          o->next = list->next;
          list->next = o;
        }
      
    } else if (object) {
        //有object、没通知名
        
        n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)object);
        if (n == 0) {
          o->next = ENDOBS;
          GSIMapAddPair(NAMELESS, (GSIMapKey)object, (GSIMapVal)o);
        } else {
          list = (Observation*)n->value.ptr;
          o->next = list->next;
          list->next = o;
        }
    } else {
        //没有name又没有object,放在wildcard链表中
          o->next = WILDCARD;
          WILDCARD = o;
    }
    //解锁
  unlockNCTable(TABLE);
}
根据observer、selector创建一个observation是否存在name(通知名) 有name,以name为key在named表中找到相应的value,value也是一个表,再以object为key取出列表,将observation添加进去,结束没有name,第三步 是否有object 有object,在nameless表中找以object为key的value,将observation添加进入,结束没有object,第四步 到这里就是没通知名没指定发通知的对象,把observation添加到wildcard中 发送通知

三种

- (void) postNotification: (NSNotification*)notification
{
  if (notification == nil)
    {
      [NSException raise: NSInvalidArgumentException
		  format: @"Tried to post a nil notification."];
    }
  [self _postAndRelease: RETAIN(notification)];
}
- (void) postNotificationName: (NSString*)name
		       object: (id)object
{
  [self postNotificationName: name object: object userInfo: nil];
}
- (void) postNotificationName: (NSString*)name
		       object: (id)object
		     userInfo: (NSDictionary*)info
{
  GSNotification	*notification;

  notification = (id)NSAllocateObject(concrete, 0, NSDefaultMallocZone());
  notification->_name = [name copyWithZone: [self zone]];
  notification->_object = [object retain];
  notification->_info = [info retain];
  [self _postAndRelease: notification];
}

其实最终内部都是调用的[self _postAndRelease: notification];

postAndRelease实现

- (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;
    
    //name为空 报错
  if (name == nil)
    {
      RELEASE(notification);
      [NSException raise: NSInvalidArgumentException
		  format: @"Tried to post a notification with no name."];
    }
  object = [notification object];

  /*
   * Lock the table of observations while we traverse it.
   *
   * The table of observations contains weak pointers which are zeroed when
   * the observers get garbage collected.  So to avoid consistency problems
   * we disable gc while we copy all the observations we are interested in.
   * We use scanned memory in the array in the case where there are more
   * than the 64 observers we allowed room for on the stack.
   */
  GSIArrayInitWithZoneAndStaticCapacity(a, _zone, 64, i);
  lockNCTable(TABLE);

  /*
   * Find all the observers that specified neither NAME nor OBJECT.
   */
    //查找没有指定name、object的观察者,加在a数组中
    for (o = WILDCARD = purgeCollected(WILDCARD); o != ENDOBS; o = o->next) {
      GSIArrayAddItem(a, (GSIArrayItem)o);
    }

  /*
   * Find the observers that specified OBJECT, but didn't specify NAME.
   */
    //查找指定了OBJECT但没有指定NAME的观察者。
    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).
   //查找name的观察者,
   */
  if (name) {
      //根据name(key)找到value
      n = GSIMapNodeForKey(NAMED, (GSIMapKey)((id)name));
      if (n) {
          m = (GSIMapTable)n->value.ptr;
      } else {
          m = 0;
      }
      if (m != 0) {
          /*
           * First, observers with a matching object.
           */
          //在name对应的表中,根据object(key)找到对应的链表, 通过遍历找到object相同的观察者
          n = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
          if (n != 0) {
              o = purgeCollectedFromMapNode(m, n);
              while (o != ENDOBS) {
                  GSIArrayAddItem(a, (GSIArrayItem)o);
                  o = o->next;
              }
          }
          
          if (object != nil) {
              /*
               * Now observers with a nil object.
               */
              //再找没有object的观察者
              n = GSIMapNodeForSimpleKey(m, (GSIMapKey)nil);
              if (n != 0) {
                  o = purgeCollectedFromMapNode(m, n);
                  while (o != ENDOBS)
                  {
                      GSIArrayAddItem(a, (GSIArrayItem)o);
                      o = o->next;
                  }
              }
          }
      }
    }

  /* Finished with the table ... we can unlock it,
   */
  unlockNCTable(TABLE);

  /*
   //发送所有通知
   * Now send all the notifications.
   */
  count = GSIArrayCount(a);
  while (count-- > 0)
    {
      o = GSIArrayItemAtIndex(a, count).ext;
      if (o->next != 0)
	{
          NS_DURING
            {
                //观察者执行selector方法
                [o->observer performSelector: o->selector
                                withObject: notification];
            }
          NS_HANDLER
            {
              NSLog(@"Problem posting notification: %@", localException);
            }
          NS_ENDHANDLER
	}
    }
  lockNCTable(TABLE);
  GSIArrayEmpty(a);
  unlockNCTable(TABLE);

  RELEASE(notification);
}
创建一个数组用来存储需要发送通知的观察者(a数组)先遍历wildcard(无name、无object),将所有的observation添加在观察者数组中nameless表中,根据object找到对应的链表,将所有结点放入观察者数组中在named表中,根据name找到对应的value,在value中再根据object找到对应的链表,将链表中所有的结点放入观察者数组中。再以将nil为key找到对应的链表遍历链表,将observer放入观察者数组中最后!!发送所有通知,遍历观察者数组,让observer调用相应的selector(perform:selector,所以发送通知的线程和接收通知的回调方法的线程是一个线程) 移除通知
- (void) removeObserver: (id)observer
{
  if (observer == nil)
    return;

  [self removeObserver: observer name: nil object: nil];
}
- (void) removeObserver: (id)observer
		   name: (NSString*)name
                 object: (id)object
{
    
    if (name == nil && object == nil && observer == nil)
        return;
    
    /*
     *	NB. The removal algorithm depends on an implementation characteristic
     *	of our map tables - while enumerating a table, it is safe to remove
     *	the entry returned by the enumerator.
     */
    
    lockNCTable(TABLE);
    //name和object都为nil,删除链表中 observer结点
    if (name == nil && object == nil)
    {
        WILDCARD = listPurge(WILDCARD, observer);
    }
    
    //name为nil
    if (name == nil)
    {
        GSIMapEnumerator_t	e0;
        GSIMapNode		n0;
        
        /*
         首先尝试删除为该对象设置的所有已命名项。
         * First try removing all named items set for this object.
         */
        e0 = GSIMapEnumeratorForMap(NAMED);
        n0 = GSIMapEnumeratorNextNode(&e0);
        while (n0 != 0)
        {
            GSIMapTable		m = (GSIMapTable)n0->value.ptr;
            NSString		*thisName = (NSString*)n0->key.obj;
            
            n0 = GSIMapEnumeratorNextNode(&e0);
            //name为空、object为空,清空named表
            if (object == nil)
            {
                GSIMapEnumerator_t	e1 = GSIMapEnumeratorForMap(m);
                GSIMapNode		n1 = GSIMapEnumeratorNextNode(&e1);
                
                /*
                 * Nil object and nil name, so we step through all the maps
                 * keyed under the current name and remove all the objects
                 * that match the observer.
                 */
                while (n1 != 0)
                {
                    GSIMapNode	next = GSIMapEnumeratorNextNode(&e1);
                    
                    purgeMapNode(m, n1, observer);
                    n1 = next;
                }
            } else {  //name为空、object不为空,找到对应object(key)对应的链表,清空链表
                GSIMapNode	n1;
                
                /*
                 * Nil name, but non-nil object - we locate the map for the
                 * specified object, and remove all the items that match
                 * the observer.
                 */
                n1 = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
                if (n1 != 0)
                {
                    purgeMapNode(m, n1, observer);
                }
            }
            /*
             * If we removed all the observations keyed under this name, we
             * must remove the map table too.
             */
            if (m->nodeCount == 0)
            {
                mapFree(TABLE, m);
                GSIMapRemoveKey(NAMED, (GSIMapKey)(id)thisName);
            }
        }
        
        /*
         开始删除nameless表
         * Now remove unnamed items
         */
        //object为空,清空nameless表
        if (object == nil)
        {
            e0 = GSIMapEnumeratorForMap(NAMELESS);
            n0 = GSIMapEnumeratorNextNode(&e0);
            while (n0 != 0)
            {
                GSIMapNode	next = GSIMapEnumeratorNextNode(&e0);
                
                purgeMapNode(NAMELESS, n0, observer);
                n0 = next;
            }
        } else { //object不为空,根据object找到对应的链表,清空链表
            n0 = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)object);
            if (n0 != 0)
            {
                purgeMapNode(NAMELESS, n0, observer);
            }
        }
    } else {  //name不为nil
        
        GSIMapTable		m;
        GSIMapEnumerator_t	e0;
        GSIMapNode		n0;
        
        /*
         * Locate the map table for this name.
         */
        n0 = GSIMapNodeForKey(NAMED, (GSIMapKey)((id)name));
        //named表中没有那name相同的key 直接返回
        if (n0 == 0)
        {
            unlockNCTable(TABLE);
            return;		/* Nothing to do.	*/
        }
        m = (GSIMapTable)n0->value.ptr;
        
        //object为空,清空named中name(key)对应的value
        if (object == nil)
        {
            e0 = GSIMapEnumeratorForMap(m);
            n0 = GSIMapEnumeratorNextNode(&e0);
            
            while (n0 != 0)
            {
                GSIMapNode	next = GSIMapEnumeratorNextNode(&e0);
                
                purgeMapNode(m, n0, observer);
                n0 = next;
            }
        } else {//object不为空 object为key找到对应的链表 清空
            n0 = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
            if (n0 != 0)
            {
                purgeMapNode(m, n0, observer);
            }
        }
        //删除named表中name这个key
        if (m->nodeCount == 0)
        {
            mapFree(TABLE, m);
            GSIMapRemoveKey(NAMED, (GSIMapKey)((id)name));
        }
    }
    unlockNCTable(TABLE);
}

removeObserver: (id)observer name: (NSString*)name object: (id)object

通知名NotificationName为空、指定发通知的对象object为空,则清空wildcard表通知名NotificationName为空 开始清named表: object也为空,清空named表object不为空,清空所有以object为key的链表 开始清nameless表: object也为空,清空nameless表object不为空,清空object对应的链表 通知名NotificationName不为空 object为空,清空通知名对应的所有valueobject不为空,根据通知名(key)获取相应value,value中根据object(key)获取相应的链表,清空链表 一些问题 通知中心发送通知给观察者是同步的

也可以用通知队列(NSNoyificationQueue)异步发送通知。
接收通知的线程 和发送通知的线程是同一个线程。

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"NotificationName" object:nil];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{    // 异步执行 + 串行队列
        NSLog(@"--current thread: %@", [NSThread currentThread]);
        NSLog(@"Begin post notification");
        [[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationName" object:nil];
        NSLog(@"End");
    }); 
}

- (void)test {
    NSLog(@"--current thread: %@", [NSThread currentThread]);
    NSLog(@"Handle notification and sleep 3s");
    sleep(3);
}

怎样异步? 在通知时间处理方法中,开启子线程,将 *** 作放进子线程中
- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"NotificationName" object:nil];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"--current thread: %@", [NSThread currentThread]);
    NSLog(@"Begin post notification");
    [[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationName" object:nil];
    NSLog(@"End");
}

- (void)test {
    dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{    // 异步执行 + 串行队列
        NSLog(@"--current thread: %@", [NSThread currentThread]);
        NSLog(@"Handle notification and sleep 3s");
        sleep(3);
    });
}

使用通知队列 (NSNotificationQueue)
- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"NotificationName" object:nil];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"--current thread: %@", [NSThread currentThread]);
    NSLog(@"Begin post notification");
    NSNotification *notification = [NSNotification notificationWithName:@"NotificationName"
                                                                     object:nil];
    [[NSNotificationQueue defaultQueue] enqueueNotification:notification postingStyle:NSPostASAP];
    NSLog(@"End");
}

- (void)test {
    NSLog(@"--current thread: %@", [NSThread currentThread]);
    NSLog(@"Handle notification and sleep 3s");
    sleep(3);
}

自己在子线程中用通知队列没效果,又是和runloop有关…

[[NSNotificationQueue defaultQueue] enqueueNotification:notification postingStyle:NSPostASAP];

页面销毁时不移除通知会崩溃吗

在观察者对象释放之前,需要调用 removeOberver 方法将观察者从通知中心移除,否则程序可能会出现崩溃。但从 iOS9 开始,即使不移除观察者对象,程序也不会出现异常。

这是因为在 iOS9 以后,通知中心持有的观察者由 unsafe_unretained 引用变为 weak 引用。即使不对观察者手动移除,持有的观察者的引用也会在观察者被回收后自动置空。但是通过 addObserverForName:object: queue:usingBlock: 方法注册的观察者需要手动释放,因为通知中心持有的是它们的强引用。

多次添加同一个通知会是什么结果?多次移除通知? 多次添加同一个通知,观察者方法会调用多次多次移除,没关系。为什么?? 下面的方法不会接收到通知?
// 添加观察
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"TestNotification" object:@1];
// 通知发送
[NSNotificationCenter.defaultCenter postNotificationName:@"TestNotification" object:nil];

不会,上文介绍 NSNotificationCenter 时介绍了 center 的结构。

注册通知在添加observer时,路径为 TestNotification -> @1 -> self发送通知在查找observer时,路径为 TestNotification -> nil -> observer list

参考
学姐的博客

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/996760.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存