iOS源码解析: NotificationCenter是如何实现的?

4,509 阅读21分钟

本文从源码角度对iOS中的通知进行了解析,并对通知中心的一些特性进行了相应的解读。

Swift版本

数据结构

NSNotification

NSNotification理所当然要包含通知name、object,且使用userInfo用于传递参数。

open class NSNotification: NSObject, NSCopying, NSCoding {
    public struct Name : RawRepresentable, Equatable, Hashable {
        public private(set) var rawValue: String

        public init(_ rawValue: String) {
            self.rawValue = rawValue
        }

        public init(rawValue: String) {
            self.rawValue = rawValue
        }
    }

    private(set) open var name: Name
    
    private(set) open var object: Any?
    
    private(set) open var userInfo: [AnyHashable : Any]?
}

这里封装了一个结构体Name,而非直接使用字符串。所以,我们通常使用的话,需要这样写 ***NotificationCenter.default.post(name: NSNotification.Name(rawValue: kNotificationCLLocationDidUpdated), object: nil)***。

基于Swift的特点,我们可以对Swift项目中的相关Notification使用进行一些优雅的改进。

更加Swift化的通知

强烈建议采用Alamofire中这样的写法:

extension Notification.Name {
    /// Used as a namespace for all `URLSessionTask` related notifications.
    public struct Task {
        /// Posted when a `URLSessionTask` is resumed. The notification `object` contains the resumed `URLSessionTask`.
        public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume")

        /// Posted when a `URLSessionTask` is suspended. The notification `object` contains the suspended `URLSessionTask`.
        public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend")

        /// Posted when a `URLSessionTask` is cancelled. The notification `object` contains the cancelled `URLSessionTask`.
        public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel")

        /// Posted when a `URLSessionTask` is completed. The notification `object` contains the completed `URLSessionTask`.
        public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete")
    }
}

则使用方式就很简单了,且通过类似 Notification.Name.Task 这样的写法来进行了业务的区分。

NotificationCenter.default.post(
                name: Notification.Name.Task.DidComplete,
                object: strongSelf,
                userInfo: userInfo
            )

NSNotificationReceiver

NSNotificationReceiver用于封装通知的基本结构,name、block、sender都包含在里边。这里的name使用的其实就是Notification.Name对象,sender即为发送通知的对象。

private class NSNotificationReceiver : NSObject {
    fileprivate var name: Notification.Name?
    fileprivate var block: ((Notification) -> Void)?
    fileprivate var sender: AnyObject?
    fileprivate var queue: OperationQueue?
}

NotificationCenter中使用_observers来存储NSNotificationReceiver对象。

private var _observers: [AnyHashable /* Notification.Name */ : [ObjectIdentifier /* object */ : [ObjectIdentifier /* notification receiver */ : NSNotificationReceiver]]]

简化一下就是

private var _observers: [AnyHashable : [ObjectIdentifier : [ObjectIdentifier : NSNotificationReceiver]]]

为啥要使用这么复杂的结构?而不是直接使用 [Name: [NSNotificationReceiver]]这样的结构,原因在于区分通知的不仅仅是name,还有发送对象即object。一个通知名(String)可以对应于多个通知Receiver。

_observers的key是一个可hash的对象,value是一个字典。该value字典的key是ObjectIdentifier,而value则是[ObjectIdentifier : NSNotificationReceiver],又是一个字典。

后续会讲到ObjectIdentifier,这里仅简单理解为唯一标记一个对象(通知的sender)即可。

addObserver

addObserver将通知加到通知中心。

open func addObserver(forName name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, using block: @escaping (Notification) -> Void) -> NSObjectProtocol {
    let newObserver = NSNotificationReceiver()
    newObserver.name = name
    newObserver.block = block
    newObserver.sender = __SwiftValue.store(obj)
    newObserver.queue = queue
    
    let notificationNameIdentifier: AnyHashable = name.map({ AnyHashable($0) }) ?? _nilHashable
    let senderIdentifier: ObjectIdentifier = newObserver.sender.map({ ObjectIdentifier($0) }) ?? _nilIdentifier
    let receiverIdentifier: ObjectIdentifier = ObjectIdentifier(newObserver)

    _observersLock.synchronized({
        _observers[notificationNameIdentifier, default: [:]][senderIdentifier, default: [:]][receiverIdentifier] = newObserver
    })
    
    return newObserver
}

先封装一个NSNotificationReceiver对象,注意这里对发送通知的对象obj使用了__SwiftValue.store(obj)操作,存到了sender中。

将name转换为notificationNameIdentifier,将newObserver.sender转为为ObjectIdentifier对象senderIdentifier。

注意为nil的时候,分别转换成了_nilHashable和_nilIdentifier,即:

private lazy var _nilIdentifier: ObjectIdentifier = ObjectIdentifier(_observersLock)
private lazy var _nilHashable: AnyHashable = AnyHashable(_nilIdentifier)

开发者文档是这样解释的:

name: The name of the notification for which to register the observer; that is, only notifications with this name are used to add the block to the operation queue. If you pass nil, the notification center doesn’t use a notification’s name to decide whether to add the block to the operation queue.

obj:The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer. If you pass nil, the notification center doesn’t use a notification’s sender to decide whether to deliver it to the observer.

name用于唯一标识一个通知,obj为发送通知的对象。如果name和obj都为nil,则addObserver会注册一个observer,对所有的通知进行响应。如:

[[NSNotificationCenter defaultCenter] addObserverForName:nil object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
    NSLog(@"block 3 %@", note.name);
    sleep(2);
}];

以上代码会对所有通知进行响应,打印结果如下:

2020-04-14 23:11:40.027720+0800 DemoRunloop[15128:4831118] block 3 NSThreadWillExitNotification
2020-04-14 23:11:41.188560+0800 DemoRunloop[15128:4830986] block 3 1234567
2020-04-14 23:11:43.189717+0800 DemoRunloop[15128:4830986] block 3 UIApplicationDidFinishLaunchingNotification
2020-04-14 23:11:45.171794+0800 DemoRunloop[15128:4831116] block 3 NSThreadWillExitNotification
2020-04-14 23:11:45.190932+0800 DemoRunloop[15128:4830986] block 3 UIApplicationSuspendedNotification
2020-04-14 23:11:47.191469+0800 DemoRunloop[15128:4830986] block 3 _UIWindowContentWillRotateNotification
2020-04-14 23:11:47.244987+0800 DemoRunloop[15128:4831122] block 3 NSThreadWillExitNotification
2020-04-14 23:11:49.192743+0800 DemoRunloop[15128:4830986] block 3 UIDeviceOrientationDidChangeNotification
2020-04-14 23:11:51.194071+0800 DemoRunloop[15128:4830986] block 3 _UIApplicationDidRemoveDeactivationReasonNotification

继续往下看,就是将封装好的NSNotificationReceiver对象存储到_observers中。

_observers[notificationNameIdentifier, default: [:]][senderIdentifier, default: [:]][receiverIdentifier] = newObserver

对照着定义:

private var _observers: [AnyHashable : [ObjectIdentifier : [ObjectIdentifier : NSNotificationReceiver]]]

Swift中的Dictionary可以使用default来指定默认值。

removeObserver

removeObserver即根据_observers执行相应的移除操作。

open func removeObserver(_ observer: Any, name aName: NSNotification.Name?, object: Any?) {
    guard let observer = observer as? NSNotificationReceiver,
        // These 2 parameters would only be useful for removing notifications added by `addObserver:selector:name:object:`
        aName == nil || observer.name == aName,
        object == nil || observer.sender === __SwiftValue.store(object)
    else {
        return
    }

    let notificationNameIdentifier: AnyHashable = observer.name.map { AnyHashable($0) } ?? _nilHashable
    let senderIdentifier: ObjectIdentifier = observer.sender.map { ObjectIdentifier($0) } ?? _nilIdentifier
    let receiverIdentifier: ObjectIdentifier = ObjectIdentifier(observer)
    
    _observersLock.synchronized({
        _observers[notificationNameIdentifier]?[senderIdentifier]?.removeValue(forKey: receiverIdentifier)
        if _observers[notificationNameIdentifier]?[senderIdentifier]?.count == 0 {
            _observers[notificationNameIdentifier]?.removeValue(forKey: senderIdentifier)
        }
    })
}

postNotification

postNotification的目的很简单,就是遍历存储的所有NSNotificationReceiver对象,找到符合条件的,将通知发送到Receiver即可。

open func post(name aName: NSNotification.Name, object anObject: Any?, userInfo aUserInfo: [AnyHashable : Any]? = nil) {
    let notification = Notification(name: aName, object: anObject, userInfo: aUserInfo)
    post(notification)
}

先构建一个Notification对象,然后调用post函数:

open func post(_ notification: Notification) {
    let notificationNameIdentifier: AnyHashable = AnyHashable(notification.name)
    // 转换成ObjectIdentifier
    let senderIdentifier: ObjectIdentifier? = notification.object.map({ ObjectIdentifier(__SwiftValue.store($0)) })
    
    let sendTo: [Dictionary<ObjectIdentifier, NSNotificationReceiver>.Values] = _observersLock.synchronized({
        var retVal = [Dictionary<ObjectIdentifier, NSNotificationReceiver>.Values]()
        (_observers[_nilHashable]?[_nilIdentifier]?.values).map({ retVal.append($0) })
        senderIdentifier.flatMap({ _observers[_nilHashable]?[$0]?.values }).map({ retVal.append($0) })
        (_observers[notificationNameIdentifier]?[_nilIdentifier]?.values).map({ retVal.append($0) })
        senderIdentifier.flatMap({ _observers[notificationNameIdentifier]?[$0]?.values}).map({ retVal.append($0) })
        
        return retVal
    })

    sendTo.forEach { observers in
        observers.forEach { observer in
            guard let block = observer.block else {
                return
            }
            
            if let queue = observer.queue, queue != OperationQueue.current {
                queue.addOperation { block(notification) }
                queue.waitUntilAllOperationsAreFinished()
            } else {
                block(notification)
            }
        }
    }
}

原理其实很简单:根据通知name和sender来查找对应的Receiver,然后调用其响应即可。然而,查找的过程看着相当累。。。

这句代码 let senderIdentifier: ObjectIdentifier? = notification.object.map({ ObjectIdentifier(__SwiftValue.store($0)) }) 要与addObserver中的 newObserver.sender = __SwiftValue.store(obj) 结合起来看,就是根据Notification的object对象(也就是通知的sender),转换为一个ObjectIdentifier唯一表示。并且,再一次强调一下 _observers 这个变态的字典。

private var _observers: [AnyHashable : [ObjectIdentifier : [ObjectIdentifier : NSNotificationReceiver]]]

下边的一句更变态,sendTo实际上是一个数组,其值就是字典的Values,即sendTo是NSNotificationReceiver组成的数组。添加一些注释:

let sendTo: [Dictionary<ObjectIdentifier, NSNotificationReceiver>.Values] = _observersLock.synchronized({
    // 初始化一个空数组
    var retVal = [Dictionary<ObjectIdentifier, NSNotificationReceiver>.Values]()
    // name为nil的observer,通过_nilHashable为key来查找,即 _observers[_nilHashable]? 结果为 [ObjectIdentifier : [ObjectIdentifier : NSNotificationReceiver]]
    // name为nil且object为nil的observer,通过 _nilIdentifier为key来查找,即 _observers[_nilHashable]?[_nilIdentifier]? 结果为 [ObjectIdentifier : NSNotificationReceiver]
    // 不区分name和object的observer,都会收到该通知
    (_observers[_nilHashable]?[_nilIdentifier]?.values).map({ retVal.append($0) })
    // 没有name的observer(即_observers[_nilHashable]?),若ObjectIdentifier为senderIdentifier,则会收到该通知
    senderIdentifier.flatMap({ _observers[_nilHashable]?[$0]?.values }).map({ retVal.append($0) })
    // name符合的observer(即_observers[notificationNameIdentifier]?),若object为nil(即_observers[notificationNameIdentifier]?[_nilIdentifier]?),则会受到该通知
    (_observers[notificationNameIdentifier]?[_nilIdentifier]?.values).map({ retVal.append($0) })
    // name和object均符合的observer,则会收到该通知
    senderIdentifier.flatMap({ _observers[notificationNameIdentifier]?[$0]?.values}).map({ retVal.append($0) })
    
    return retVal
})

使用Swift的高阶函数,写了这么大一堆代码,实际上就是为了过滤出满足条件的NSNotificationReceiver而已:

  1. name和object均为nil
  2. name为nil,object符合
  3. name符合,object为nil
  4. name和object均符合

我个人觉得这种写法给我带来了不少困惑,直接一个for循环加上判断不就解决了么?想更加Swift的话,使用 _observers.filter({ 筛选出符合条件的observer }) 不就可以了么?何必如此复杂。

总之,得到了NSNotificationReceiver数组sendTo之后,就是执行observer任务的时候了:

sendTo.forEach { observers in
  observers.forEach { observer in
      guard let block = observer.block else {
          return
      }
      
      if let queue = observer.queue, queue != OperationQueue.current {
          queue.addOperation { block(notification) }
          queue.waitUntilAllOperationsAreFinished()
      } else {
          block(notification)
      }
  }
}

如果observer指定了queue,且与当前post的queue一致,则将任务加到queue,调用 queue.waitUntilAllOperationsAreFinished() 会将当前的任务执行完毕,才会对下一个observer任务执行addOperation。

如果没有指定queue,则直接执行observer的任务。所以observer的任务执行的线程,依然与当前post的保持一致。这也是iOS中通知中心非常重要的一点!!!

遗留的问题

ObjectIdentifier

/// A unique identifier for a class instance or metatype.
///
/// In Swift, only class instances and metatypes have unique identities. There
/// is no notion of identity for structs, enums, functions, or tuples.

用于唯一表示一个类的实例或者metatype。

__SwiftValue

// TODO: Making this a SwiftObject subclass would let us use Swift refcounting,
// but we would need to be able to emit __SwiftValue's Objective-C class object
// with the Swift destructor pointer prefixed before it.
//
// The layout of `__SwiftValue` is:
// - object header,
// - `SwiftValueHeader` instance,
// - the payload, tail-allocated (the Swift value contained in this box).
//
// NOTE: older runtimes called this _SwiftValue. The two must
// coexist, so it was renamed. The old name must not be used in the new
// runtime.
@interface __SwiftValue : NSObject <NSCopying>

- (id)copyWithZone:(NSZone *)zone;

@end

关于__SwiftValue,可以参考 奇怪的AnyObject和背后的SwiftValue

Objective-C版本

鉴于Objective-C版本与Swift版本完全不一样,这里也一并进行解析。这里采用GNUStep的源码。

数据结构

NSNotification

结构基本类似:

@interface NSNotification : NSObject <NSCopying, NSCoding>
- (NSString*) name;
- (id) object;
- (NSDictionary*) userInfo;
@end

@implementation NSNotification

static Class	abstractClass = 0;
static Class	concreteClass = 0;

+ (void) initialize
{
  if (concreteClass == 0)
    {
      abstractClass = [NSNotification class];
      concreteClass = [GSNotification class];
    }
}

+ (NSNotification*) notificationWithName: (NSString*)name
				  object: (id)object
			        userInfo: (NSDictionary*)info
{
  return [concreteClass notificationWithName: name
				      object: object
				    userInfo: info];
}
@end

看GSNotification:

/**
 * Concrete class implementing NSNotification.
 */
@interface	GSNotification : NSNotification
{
@public
  NSString	*_name;
  id		_object;
  NSDictionary	*_info;
}
@end

@implementation GSNotification
+ (NSNotification*) notificationWithName: (NSString*)name
				  object: (id)object
			        userInfo: (NSDictionary*)info
{
  GSNotification	*n;

  n = (GSNotification*)NSAllocateObject(self, 0, NSDefaultMallocZone());
  n->_name = [name copyWithZone: [self zone]];
  n->_object = TEST_RETAIN(object);
  n->_info = TEST_RETAIN(info);
  return AUTORELEASE(n);
}
@end

初始化的通知对象是GSNotification,包含了name、object、info。

Observation

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;

Observation封装了observer、selector,通过next指针构成了一个链表结构。相当于Swift版本的NSNotificationReceiver。

NCTable

NCTable存储了所有的Observation对象。

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;

nameless是负责没有name的通知,而named则是有name的。

  1. wildcard存储name和object都为nil的observer
  2. nameless存储name为nil,但object不为nil的observer
  3. named存储name不为nil的observer

freeList、chunks、cache用于查找observer时的缓存机制,提高查找效率。

addObserver

GSNotificationObserver

@interface GSNotificationObserver : NSObject
{
	NSOperationQueue *_queue;
	GSNotificationBlock _block;
}
@end

GSNotificationObserver对象包含了block。如果使用了 addObserverForName:object:queue:usingBlock: 接口,会先封装一个GSNotificationObserver对象,然后再调用 addObserver:selector:name:object: 接口,传入的selector即为 @selector(didReceiveNotification:)

- (id) addObserverForName: (NSString *)name 
                   object: (id)object 
                    queue: (NSOperationQueue *)queue 
               usingBlock: (GSNotificationBlock)block
{
	GSNotificationObserver *observer = 
		[[GSNotificationObserver alloc] initWithQueue: queue block: block];

	[self addObserver: observer 
	         selector: @selector(didReceiveNotification:) 
	             name: name 
	           object: object];

	return observer;
}

didReceiveNotification如下,

- (void) didReceiveNotification: (NSNotification *)notif
{
	if (_queue != nil)
	{
		GSNotificationBlockOperation *op = [[GSNotificationBlockOperation alloc] 
			initWithNotification: notif block: _block];

		[_queue addOperation: op];
	}
	else
	{
		CALL_BLOCK(_block, notif);
	}
}

如果指定了queue,则将NSNotification对象和GSNotificationObserver对象的_block包装成了GSNotificationBlockOperation,然后加入到queue中,由queue来调度执行。如果未指定queue,则直接 CALL_BLOCK(_block, notif); 来调用。

GSNotificationBlockOperation的任务,其实也就是 ***CALL_BLOCK(_block, _notification);***。

@implementation GSNotificationBlockOperation
- (id) initWithNotification: (NSNotification *)notif 
                      block: (GSNotificationBlock)block
{
	self = [super init];
	if (self == nil)
		return nil;

	ASSIGN(_notification, notif);
	_block = Block_copy(block);
	return self;

}

- (void) main
{
	CALL_BLOCK(_block, _notification);
}
@end

CALL_BLOCK即为调用block,传入指定参数而已。

/**
 * Calls a block.  Works irrespective of whether the compiler supports blocks.
 */
#define CALL_BLOCK(block, args, ...) block(args, ## __VA_ARGS__)

addObserver:selector:name:object:

- (void) addObserver: (id)observer
	        selector: (SEL)selector
                name: (NSString*)name
	      object: (id)object
{
  Observation	*list;
  Observation	*o;
  GSIMapTable	m;
  GSIMapNode	n;

  if (observer == nil)
    [NSException raise: NSInvalidArgumentException
		format: @"Nil observer passed to addObserver ..."];

  if (selector == 0)
    [NSException raise: NSInvalidArgumentException
		format: @"Null selector passed to addObserver ..."];

  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);

  o = obsNew(TABLE, selector, observer);

  /*
   * Record the Observation in one of the linked lists.
   *
   * NB. It is possible to register an observer for a notification more than
   * once - in which case, the observer will receive multiple messages when
   * the notification is posted... odd, but the MacOS-X docs specify this.
   */

  if (name)
    {
      /*
       * Locate the map table for this name - create it if not present.
       */
      n = GSIMapNodeForKey(NAMED, (GSIMapKey)(id)name);
      if (n == 0)
	{
	  m = mapNew(TABLE);
	  /*
	   * As this is the first observation for the given name, we take a
	   * copy of the name so it cannot be mutated while in the map.
	   */
	  name = [name copyWithZone: NSDefaultMallocZone()];
	  GSIMapAddPair(NAMED, (GSIMapKey)(id)name, (GSIMapVal)(void*)m);
	  GS_CONSUMED(name)
	}
      else
	{
	  m = (GSIMapTable)n->value.ptr;
	}

      /*
       * Add the observation to the list for the correct object.
       */
      n = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
      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)
    {
      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
    {
      o->next = WILDCARD;
      WILDCARD = o;
    }

  unlockNCTable(TABLE);
}

通过obsNew函数,将observer、selector组成一个Observation对象,存储到_table中。根据name和object是否为nil的情况,将不同的observer对象,分别存储到不同的地方(WILDCARD、NAMELESS、NAMED)。这一点与前边Swift的实现一致。

#define	TABLE		((NCTable*)_table)
#define	WILDCARD	(TABLE->wildcard)
#define	NAMELESS	(TABLE->nameless)
#define	NAMED		(TABLE->named)
#define	LOCKCOUNT	(TABLE->lockCount)

_table = newNCTable();

obsNew函数,并非每次都新建一个Observation对象,而是从NCTable的freeList中取出空闲的对象来使用。

static Observation *
obsNew(NCTable *t, SEL s, id o)
{
  Observation	*obs;

  /* Generally, observations are cached and we create a 'new' observation
   * by retrieving from the cache or by allocating a block of observations
   * in one go.  This works nicely to both hide observations from the
   * garbage collector (when using gcc for GC) and to provide high
   * performance for situations where apps add/remove lots of observers
   * very frequently (poor design, but something which happens in the
   * real world unfortunately).
   */
  if (t->freeList == 0)
    {
      Observation	*block;

      if (t->chunkIndex == CHUNKSIZE)
	{
	  unsigned	size;

	  t->numChunks++;

	  size = t->numChunks * sizeof(Observation*);
	  t->chunks = (Observation**)NSReallocateCollectable(
	    t->chunks, size, NSScannedOption);

	  size = CHUNKSIZE * sizeof(Observation);
	  t->chunks[t->numChunks - 1]
	    = (Observation*)NSAllocateCollectable(size, 0);
	  t->chunkIndex = 0;
	}
      block = t->chunks[t->numChunks - 1];
      t->freeList = &block[t->chunkIndex];
      t->chunkIndex++;
      t->freeList->link = 0;
    }
  obs = t->freeList;
  t->freeList = (Observation*)obs->link;
  obs->link = (void*)t;
  obs->retained = 0;
  obs->next = 0;

  obs->selector = s;
  obs->observer = o;

  return obs;
}

removeObserver

/**
 * Deregisters observer for notifications matching name and/or object.  If
 * either or both is nil, they act like wildcards.  The observer may still
 * remain registered for other notifications; use -removeObserver: to remove
 * it from all.  If observer is nil, the effect is to remove all registrees
 * for the specified notifications, unless both observer and name are nil, in
 * which case nothing is done.
 */
- (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);

  if (name == nil && object == nil)
    {
      WILDCARD = listPurge(WILDCARD, observer);
    }

  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);
	  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
	    {
	      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);
	    }
	}

      /*
       * Now remove unnamed items
       */
      if (object == nil)
	{
	  e0 = GSIMapEnumeratorForMap(NAMELESS);
	  n0 = GSIMapEnumeratorNextNode(&e0);
	  while (n0 != 0)
	    {
	      GSIMapNode	next = GSIMapEnumeratorNextNode(&e0);

	      purgeMapNode(NAMELESS, n0, observer);
	      n0 = next;
	    }
	}
      else
	{
	  n0 = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)object);
	  if (n0 != 0)
	    {
	      purgeMapNode(NAMELESS, n0, observer);
	    }
	}
    }
  else
    {
      GSIMapTable		m;
      GSIMapEnumerator_t	e0;
      GSIMapNode		n0;

      /*
       * Locate the map table for this name.
       */
      n0 = GSIMapNodeForKey(NAMED, (GSIMapKey)((id)name));
      if (n0 == 0)
	{
	  unlockNCTable(TABLE);
	  return;		/* Nothing to do.	*/
	}
      m = (GSIMapTable)n0->value.ptr;

      if (object == nil)
	{
	  e0 = GSIMapEnumeratorForMap(m);
	  n0 = GSIMapEnumeratorNextNode(&e0);

	  while (n0 != 0)
	    {
	      GSIMapNode	next = GSIMapEnumeratorNextNode(&e0);

	      purgeMapNode(m, n0, observer);
	      n0 = next;
	    }
	}
      else
	{
	  n0 = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
	  if (n0 != 0)
	    {
	      purgeMapNode(m, n0, observer);
	    }
	}
      if (m->nodeCount == 0)
	{
	  mapFree(TABLE, m);
	  GSIMapRemoveKey(NAMED, (GSIMapKey)((id)name));
	}
    }
  unlockNCTable(TABLE);
}

postNotification

postNotification最终会调用_postAndRelease函数:

/**
 * Private method to perform the actual posting of a notification.
 * Release the notification before returning, or before we raise
 * any exception ... to avoid leaks.
 */
- (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];

  /*
   * 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.
   */
  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.
   */
  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)
    {
      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.
	   */
	  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.
	       */
	      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
            {
              [o->observer performSelector: o->selector
                                withObject: notification];
            }
          NS_HANDLER
            {
              NSLog(@"Problem posting notification: %@", localException);
            }
          NS_ENDHANDLER
	}
    }
  lockNCTable(TABLE);
  GSIArrayEmpty(a);
  unlockNCTable(TABLE);

  RELEASE(notification);
}

步骤:查找name和object符合条件的observer,执行其任务即可。执行任务的代码即为:***[o->observer performSelector: o->selector withObject: notification];***。

而单纯的 performSelector:withObject: 并不会跟runloop什么的扯上关系,实际上就是objc_msgSend调用,iOS的performSelector是如何实现的?

所以,OC版本的通知,一样是同步发送所有通知到observer的。

如果是对于GSNotificationObserver,则 ***[o->observer performSelector: o->selector withObject: notification];***,实际上是调用GSNotificationObserver对象的@selector(didReceiveNotification),继而执行对应的block。

NSNotificationQueue

NSNotificationQueue使得通知的发送可以在一个queue中进行。实际是将通知存入queue,然后由queue等待合适的时机进行发送,而发送实际上还是经过NSNotificationCenter。

enqueueNotification

enqueueNotification是将通知放入队列,而NSNotificationQueue的使用会依赖runloop,默认是DefaultMode。

通过两个参数决定了queue发送该通知的策略,postingStyle为发送时机,coalesceMask用于合并通知。

enum {
  NSPostWhenIdle = 1, // post when runloop is idle
  NSPostASAP = 2,     // post soon
  NSPostNow = 3       // post synchronously
};
// Posting styles into notification queue
typedef NSUInteger NSPostingStyle;

enum {
  NSNotificationNoCoalescing = 0,       // don't combine
  NSNotificationCoalescingOnName = 1,   // combine all registered with same name
  NSNotificationCoalescingOnSender = 2  // combine all registered with same object
};
// Enumeration of possible ways to combine notifications when dealing with [NSNotificationQueue]:
typedef NSUInteger NSNotificationCoalescing;

enqueueNotification如下:

/**
 *  Sets notification to be posted to notification center at time dependent on
 *  postingStyle, which may be either <code>NSPostNow</code> (synchronous
 *  post), <code>NSPostASAP</code> (post soon), or <code>NSPostWhenIdle</code>
 *  (post when runloop is idle).  coalesceMask determines whether this
 *  notification should be considered same as other ones already on the queue,
 *  in which case they are removed through a call to
 *  -dequeueNotificationsMatching:coalesceMask: .  The modes argument
 *  determines which [NSRunLoop] mode notification may be posted in (nil means
 *  NSDefaultRunLoopMode).
 */
- (void) enqueueNotification: (NSNotification*)notification
		postingStyle: (NSPostingStyle)postingStyle
		coalesceMask: (NSUInteger)coalesceMask
		    forModes: (NSArray*)modes
{
  if (modes == nil)
    {
      modes = defaultMode;
    }
  if (coalesceMask != NSNotificationNoCoalescing)
    {
      [self dequeueNotificationsMatching: notification
			    coalesceMask: coalesceMask];
    }
  switch (postingStyle)
    {
      case NSPostNow:
	{
	  NSString	*mode;

	  mode = [[NSRunLoop currentRunLoop] currentMode];
	  if (mode == nil || [modes indexOfObject: mode] != NSNotFound)
	    {
	      [_center postNotification: notification];
	    }
	}
	break;

      case NSPostASAP:
	add_to_queue(_asapQueue, notification, modes, _zone);
	break;

      case NSPostWhenIdle:
	add_to_queue(_idleQueue, notification, modes, _zone);
	break;
    }
}

如果选择了合并通知,则会调用dequeueNotificationsMatching:coalesceMask进行合并操作。

如果设置为NSPostNow,则立即调用postNotification方法。否则,调用add_to_queue将通知放入不同的queue。

static void
add_to_queue(NSNotificationQueueList *queue, NSNotification *notification,
  NSArray *modes, NSZone *_zone)
{
  NSNotificationQueueRegistration	*item;

  item = NSZoneCalloc(_zone, 1, sizeof(NSNotificationQueueRegistration));
  if (item == 0)
    {
      [NSException raise: NSMallocException
      		  format: @"Unable to add to notification queue"];
    }

  item->notification = RETAIN(notification);
  item->name = [notification name];
  item->object = [notification object];
  item->modes = [modes copyWithZone: [modes zone]];

  item->next = NULL;
  item->prev = queue->tail;
  queue->tail = item;
  if (item->prev)
    {
      item->prev->next = item;
    }
  if (!queue->head)
    {
      queue->head = item;
    }
}

_asapQueue和_idleQueue两个queue实际上是NSNotificationQueueList

typedef struct _NSNotificationQueueList
{
  struct _NSNotificationQueueRegistration	*head;
  struct _NSNotificationQueueRegistration	*tail;
} NSNotificationQueueList;

NSNotificationQueue中保存的实际上NSNotificationQueueRegistration对象构成的链表。

runloop如何发送通知的?

runloop会通过调用下边三个函数,分别将不同的queue中的通知发送出来。而这三个函数在runloop中的 acceptInputForMode:beforeDate: 函数中会触发,即:

GSPrivateNotifyASAP(_currentMode); 或 GSPrivateNotifyIdle(_currentMode);
[self acceptInputForMode: mode beforeDate: d];
- (BOOL) runMode: (NSString*)mode beforeDate: (NSDate*)date
/* Function used by the NSRunLoop and friends for processing
 * queued notifications which should be processed at the first safe moment.
 */
void GSPrivateNotifyASAP(NSString *mode) GS_ATTRIB_PRIVATE;

/* Function used by the NSRunLoop and friends for processing
 * queued notifications which should be processed when the loop is idle.
 */
void GSPrivateNotifyIdle(NSString *mode) GS_ATTRIB_PRIVATE;

/* Function used by the NSRunLoop and friends for determining whether
 * there are more queued notifications to be processed.
 */
BOOL GSPrivateNotifyMore(NSString *mode) GS_ATTRIB_PRIVATE;
/*
 *	The following code handles sending of queued notifications by
 *	NSRunLoop.
 */

void
GSPrivateNotifyASAP(NSString *mode)
{
  NotificationQueueList	*item;

  GSPrivateCheckTasks();

  for (item = currentList(); item; item = item->next)
    {
      if (item->queue)
	{
	  notify(item->queue->_center,
	    item->queue->_asapQueue,
	    mode,
	    item->queue->_zone);
	}
    }
}

void
GSPrivateNotifyIdle(NSString *mode)
{
  NotificationQueueList	*item;

  for (item = currentList(); item; item = item->next)
    {
      if (item->queue)
	{
	  notify(item->queue->_center,
	    item->queue->_idleQueue,
	    mode,
	    item->queue->_zone);
	}
    }
}

BOOL
GSPrivateNotifyMore(NSString *mode)
{
  NotificationQueueList	*item;

  for (item = currentList(); item; item = item->next)
    {
      if (item->queue != nil)
	{
          NSNotificationQueueRegistration	*r;

	  r = item->queue->_idleQueue->head;
	  while (r != 0)
	    {
	      if (mode == nil || [r->modes indexOfObject: mode] != NSNotFound)
		{
		  return YES;
		}
	      r = r->next;
	    }
	}
    }
  return NO;
}

发送操作则是notify函数。

static void
notify(NSNotificationCenter *center, NSNotificationQueueList *list,
  NSString *mode, NSZone *zone)
{
  BOOL					allocated = NO;
  void					*buf[100];
  void					**ptr = buf;
  unsigned				len = sizeof(buf) / sizeof(*buf);
  unsigned				pos = 0;
  // 取出链表头元素
  NSNotificationQueueRegistration	*item = list->head;

  /* Gather matching items into a buffer.
   */
  while (item != 0)
    {
      if (mode == nil || [item->modes indexOfObject: mode] != NSNotFound)
	{
	  if (pos == len)
	    {
	      unsigned	want;

	      want = (len == 0) ? 2 : len * 2;
	      if (NO == allocated)
		{
		  void		*tmp;
		  
		  tmp = NSZoneMalloc(NSDefaultMallocZone(),
		    want * sizeof(void*));
		  memcpy(tmp, (void*)ptr, len * sizeof(void*));
		  ptr = tmp;
		  allocated = YES;
		}
	      else
		{
		  ptr = NSZoneRealloc(NSDefaultMallocZone(),
		    ptr, want * sizeof(void*));
		}
	      len = want;
	    }
	  ptr[pos++] = item;
	}
      item = item->next;	// head --> tail uses next link
    }
  len = pos;	// Number of items found

  /* Posting a notification catches exceptions, so it's OK to use
   * retain/release of objects here as we won't get an exception
   * causing a leak.
   */
  if (len > 0)
    {
      /* First, we make a note of each notification while removing the
       * corresponding list item from the queue ... so that when we get
       * round to posting the notifications we will not get problems
       * with another notif() trying to use the same items.
       */
      for (pos = 0; pos < len; pos++)
	{
	  item = ptr[pos];
	  ptr[pos] = RETAIN(item->notification);
	  remove_from_queue(list, item, zone);
	}

      /* Now that we no longer need to worry about r-entrancy,
       * we step through our notifications, posting each one in turn.
       */
      for (pos = 0; pos < len; pos++)
	{
	  NSNotification	*n = (NSNotification*)ptr[pos];

	  [center postNotification: n];
	  RELEASE(n);
	}

      if (allocated)
	{
	  NSZoneFree(NSDefaultMallocZone(), ptr);
	}
    }
}

发送操作实际上就是一个for循环,遍历所有NSNotification,调用NSNotificationCenter的postNotification函数即完成了通知发送。

dequeueNotification

dequeueNotification操作也会根据通知合并策略(coalesceMask)来决定出队操作。

/**
 * Immediately remove all notifications from queue matching notification on
 * name and/or object as specified by coalesce mask, which is an OR
 * ('<code>|</code>') of the options
 * <code>NSNotificationCoalescingOnName</code>,
 * <code>NSNotificationCoalescingOnSender</code> (object), and
 * <code>NSNotificationNoCoalescing</code> (match only the given instance
 * exactly).  If both of the first options are specified, notifications must
 * match on both attributes (not just either one).  Removed notifications are
 * <em>not</em> posted.
 */
- (void) dequeueNotificationsMatching: (NSNotification*)notification
			 coalesceMask: (NSUInteger)coalesceMask
{
  NSNotificationQueueRegistration	*item;
  NSNotificationQueueRegistration	*prev;
  id					name   = [notification name];
  id					object = [notification object];

  if ((coalesceMask & NSNotificationCoalescingOnName)
    && (coalesceMask & NSNotificationCoalescingOnSender))
    {
      /*
       * find in ASAP notification in queue matching both
       */
      for (item = _asapQueue->tail; item; item = prev)
	{
          prev = item->prev;
          //PENDING: should object comparison be '==' instead of isEqual?!
          if ((object == item->object) && [name isEqual: item->name])
	    {
              remove_from_queue(_asapQueue, item, _zone);
	    }
	}
      /*
       * find in idle notification in queue matching both
       */
      for (item = _idleQueue->tail; item; item = prev)
	{
          prev = item->prev;
          if ((object == item->object) && [name isEqual: item->name])
	    {
              remove_from_queue(_idleQueue, item, _zone);
	    }
	}
    }
  else if ((coalesceMask & NSNotificationCoalescingOnName))
    {
      /*
       * find in ASAP notification in queue matching name
       */
      for (item = _asapQueue->tail; item; item = prev)
	{
          prev = item->prev;
          if ([name isEqual: item->name])
	    {
              remove_from_queue(_asapQueue, item, _zone);
	    }
	}
      /*
       * find in idle notification in queue matching name
       */
      for (item = _idleQueue->tail; item; item = prev)
	{
          prev = item->prev;
          if ([name isEqual: item->name])
	    {
              remove_from_queue(_idleQueue, item, _zone);
	    }
	}
    }
  else if ((coalesceMask & NSNotificationCoalescingOnSender))
    {
      /*
       * find in ASAP notification in queue matching sender
       */
      for (item = _asapQueue->tail; item; item = prev)
	{
          prev = item->prev;
          if (object == item->object)
	    {
              remove_from_queue(_asapQueue, item, _zone);
	    }
	}
      /*
       * find in idle notification in queue matching sender
       */
      for (item = _idleQueue->tail; item; item = prev)
	{
          prev = item->prev;
          if (object == item->object)
	    {
              remove_from_queue(_idleQueue, item, _zone);
	    }
	}
    }
}

注意事项

区分通知有两个维度

基于name和object两个维度,涉及的数据结构,是通知中心的关键所在。添加、移除、发送均是基于该数据结构进行了相应的操作。

postNotification是同步的

将notification发送到所有的observer,且observer的对应任务都执行完毕,才算通知发送成功。这个逻辑其实从上边的源码已经看出来了。

Another good thing to know is that postNotificationName: posts notifications synchronously.

看下边这段代码:

static NSString *const kNotificationName = @"1234567";

- (void)postNotificationSync {
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onNotification) name:kNotificationName object:nil];
  [[NSNotificationCenter defaultCenter] addObserverForName:kNotificationName
                                                    object:nil
                                                     queue:[NSOperationQueue mainQueue]
                                                usingBlock:^(NSNotification * _Nonnull note) {
      NSLog(@"block 1");
      sleep(2);
  }];
  [[NSNotificationCenter defaultCenter] addObserverForName:kNotificationName
                                                    object:nil
                                                     queue:[NSOperationQueue mainQueue]
                                                usingBlock:^(NSNotification * _Nonnull note) {
      NSLog(@"block 2");
      sleep(2);
  }];

  NSLog(@"postNotificationName %@", [NSThread currentThread]);
  [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationName object:nil];
  NSLog(@"postNotificationName done");
}

- (void)onNotification {
  // 默认,postNotificationName和onNotification在同一个线程中执行的。
  NSLog(@"onNotification %@", [NSThread currentThread]);
  sleep(2);
}

输出结果为:

2020-04-14 21:51:28.425951+0800 DemoRunloop[11894:4768945] postNotificationName <NSThread: 0x600003a705c0>{number = 1, name = main}
2020-04-14 21:51:28.426169+0800 DemoRunloop[11894:4768945] onNotification <NSThread: 0x600003a705c0>{number = 1, name = main}
2020-04-14 21:51:30.427355+0800 DemoRunloop[11894:4768945] block 1
2020-04-14 21:51:32.428547+0800 DemoRunloop[11894:4768945] block 2
2020-04-14 21:51:34.429006+0800 DemoRunloop[11894:4768945] postNotificationName done

postNotificationName done这一句是在所有observer对应的任务执行完毕,才打印出来的。且三个任务是按照addObserver时候的顺序依次执行的,且sleep都有效。

默认postNotification和observer的任务是在同一线程

- (void)testBackgroundNotification {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onNotification) name:kNotificationName object:nil];
    self.myQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(self.myQueue, ^{
        NSLog(@"postNotificationName %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationName object:nil];
    });    
}

- (void)onNotification {
    // 默认,postNotificationName和onNotification在同一个线程中执行的。
    NSLog(@"onNotification %@", [NSThread currentThread]);
    sleep(2);
}

输出结果为:

2020-04-14 21:57:27.723143+0800 DemoRunloop[12073:4773960] postNotificationName <NSThread: 0x60000239b200>{number = 5, name = (null)}
2020-04-14 21:57:27.723352+0800 DemoRunloop[12073:4773960] onNotification <NSThread: 0x60000239b200>{number = 5, name = (null)}

如果想要指定队列执行通知的任务,可以使用 addObserverForName:object:queue:usingBlock: 的接口。

避免多线程引发的死锁

如果跨线程使用通知,要注意避免出现死锁的场景。比如,使用下边的代码,可以产生一次死锁。

@implementation MyObject
+ (instancetype)sharedInstance {
    static MyObject *sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyObject alloc] init];
        
        NSLog(@"postNotificationName %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationName object:nil];
        NSLog(@"postNotificationName done");
    });
    return sharedInstance;
}
@end

- (void)testLockDispatchOnce {
    [[NSNotificationCenter defaultCenter] addObserverForName:kNotificationName
                                                      object:nil
                                                       queue:[NSOperationQueue mainQueue]
                                                  usingBlock:^(NSNotification * _Nonnull note) {
        NSLog(@"block");
    }];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        [MyObject sharedInstance];
    });
    
    dispatch_async(dispatch_get_main_queue(), ^{
        [MyObject sharedInstance];
    });
}

死锁的原因在于:

  1. 子线程先执行了dispatch_once的逻辑,然后发送了通知。等待observer执行完任务,该通知才算发送完成,dispatch_once的代码段才能结束。
  2. 主线程执行了sharedInstance,遇到dispatch_once,要等待上一次调用dispatch_once返回,才能继续。而上一次子线程调用的dispatch_once,却因为observer的任务要在mainQueue执行,所以会等待mainQueue里边的sharedInstance调用结束。
  3. 这样就形成了死锁。所以,在dispatch_once中要避免跨线程操作。

参考资料