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

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

Swift版本

数据结构

NSNotification

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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中这样的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 这样的写法来进行了业务的区分。

1
2
3
4
5
NotificationCenter.default.post(
name: Notification.Name.Task.DidComplete,
object: strongSelf,
userInfo: userInfo
)

NSNotificationReceiver

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

1
2
3
4
5
6
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对象。

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

简化一下就是

1
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将通知加到通知中心。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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,即:

1
2
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,对所有的通知进行响应。如:

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

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

1
2
3
4
5
6
7
8
9
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中。

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

对照着定义:

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

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

removeObserver

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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即可。

1
2
3
4
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函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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 这个变态的字典。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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任务的时候了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

1
2
3
4
/// 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 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

结构基本类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 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用于封装观察者。

1
2
3
4
5
6
7
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对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
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

1
2
3
4
5
6
@interface GSNotificationObserver : NSObject
{
NSOperationQueue *_queue;
GSNotificationBlock _block;
}
@end

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (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如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (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);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@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,传入指定参数而已。

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

addObserver:selector:name:object:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
- (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的实现一致。

1
2
3
4
5
6
7
#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中取出空闲的对象来使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/**
* 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函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/**
* 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用于合并通知。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* 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