通知机制的核心是一个与线程关联的单例对象,通知中心(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,链表形式,保存多个观察者学姐博客里的图
就是个链表。
注册到这里的观察者能接收到所有的系统通知。
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
也可以用通知队列(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参考
学姐的博客
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)