问题: 简述一下Notification相关内容.
- 实现原理(结构设计、通知如何存储的、name&observer&SEL之间的关系等)
- 通知的发送时同步的,还是异步的
- NSNotificationCenter接受消息和发送消息是在一个线程里吗?如何异步发送消息
- NSNotificationQueue是异步还是同步发送?在哪个线程响应
- NSNotificationQueue和runloop的关系
- 如何保证通知接收的线程在主线程
- 页面销毁时不移除通知会崩溃吗
- 多次添加同一个通知会是什么结果?多次移除通知呢
问题: 简述一下Notification的实现原理.
Notification 相关的类主要有三个.
NSNotification
: 通知的模型,内部包含 name 、 object 、userInfo.
NSNotificationCenter
: 通知中心负责发送 NSNotification.
NSNotificationQueue
: 通知队列,负责在某些时机调用Center发送post通知.
NSNotification 底层的存储结构如下代码所示.
// 根容器,NSNotificationCenter持有
typedef struct NSTal {
Observation * wildcard; // 链表结构,保存既没有name也没有object的通知
GSIMapTable nameless; // 保存只有object的通知
GSIMapTable named; // 保存有name的通知(不管是否含有object)
} NSTable;
typedef struct Obs {
id observer; // 观察者对象,接收通知的对象
SEL selector; // 响应方法
struct Obs *next; // 链表中得下个元素
} Observation;
上面的整体存储结构如下图所示.
问题: 通知的发送是同步的,还是异步的
通知的发送是同步的,在哪一个线程发送就在哪一个线程接收,并没有开启异步线程.通知的异步发送实际上是利用RunLoop机制进行的延时发送,并不是真正意义上的异步线程发送通知.
问题: NSNotificationCenter接受消息和发送消息是在一个线程里吗?如何异步发送消息
NSNotificationCenter接受消息和发送消息是在一个线程里,想要异步发送消息,那就需要开启一个异步线程,在此线程中进行消息的发送.
问题: NSNotificationQueue是异步还是同步发送?在哪个线程响应
NSNotificationQueue是同步发送,它的异步发送更准确的说是利用RunLoop机制做的延时发送.同时消息在哪一个线程发送就在哪一个线程响应.
NSNotificationQueue的发送时机如下所示.
typedef NS_ENUM(NSUInteger, NSPostingStyle) {
NSPostWhenIdle = 1, // runloop空闲时发送
NSPostASAP = 2, // 尽快发送,这种时机是穿插在每次事件完成期间来做的
NSPostNow = 3, // 立刻发送或者合并通知完成之后发送
};
问: NSNotificationQueue和runloop的关系
NSNotificationQueue与runloop的关系是紧密相连的,NSNotificationQueue的发送时机是依赖runloop实现的.
问: 如何保证通知接收的线程在主线程?
-
在主线程中进行发送消息即可.
-
如果是在主线程中响应异步线程的通知,可用如下的API.
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block
-
NSMachPort
方式.解决主线程中响应异步线程的通知.通过在主线程的RunLoop添加machPort,设置这个port的delegate. 通过这个port, 其他线程可与主线程进行通信. 在这个port的代理方法中执行的代码肯定在主线程中运行,所以在代理方法中 NSNotificationCenter调用post方法即可.
问: 页面销毁时不移除通知会崩溃吗?
在iOS9之前, 观察中心Center对观察者的引用是unsafe_unretained
; 在iOS9之后,观察中心Center对观察者的引用是weak
.
unsafe_unretained
是不安全的. 如果对象释放,unsafe_unretained
指针是不会置为nil的,所以会造成崩溃问题.
但是 weak
在对象释放时,weak
修饰的指针会被置为nil,不会造成崩溃.
问: 多次添加同一个通知会是什么结果?多次移除通知呢
多次添加同一通知,会导致一次发送多次响应. 多次移除通知不会造成crash问题.
问: 下面的方式能接收到通知吗?为什么
// 添加通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"TestNotification" object:@1];
// 发送通知
[NSNotificationCenter.defaultCenter postNotificationName:@"TestNotification" object:nil];
不能,这个是因为 Notification 的存储逻辑导致. 我们可以看到 观察者添加通知时,同时存在name和object,所以存放的位置为 NSNotificationCenter的 named
中.
但是在发送通知时,只有 name, 没有object. 这样是会导致查找失败,最终会导致调用失败.
Comments | 0 条评论