NotificationCenter类实现原理
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>正文</li><li>1、首先定义一个NotificationCenter类定义</li><li>2、然后定义一个观察者对象</li><li>3、在NotificationCenter中添加注册观察者的方法</li><li>4、在 NotificationCenter 中添加发送通知的方法</li><li>5、在NotificationCenter中添加移除通知者的方法</li><ul class="second_class_ul"><li>NSNotificationCenter 实现</li><li>NSNotification 实现</li><li>NSNotificationQueue 的实现</li></ul></ul></div><p class="maodian"></p><h2>正文</h2><div class="cros igoods"><div class="goodsin" data-img="https://img14.360buyimg.com/pop/jfs/t1840/116/1507339532/217453/29fbde1b/5667fa5cN164bc29e.jpg" data-name="Swift程序设计实战入门" data-owner="京东自营" data-price="38" data-tgid="38" data-url="https://union-click.jd.com/jdc?e=&p=JF8BAMoJK1olXwUFU1xdCE4TBl8IG1IWVQMAUW4ZVxNJXF9RXh5UHw0cSgYYXBcIWDoXSQVJQwYCXV1VDUkSHDZNRwYlLVNYBgQfDS5yaz9VZTl3IwB6AlwraEcbM2gNHF4dXwMBZF5eDkwXAmoIK2sVXDZQOobrvpOysnPcsdTA1ZEyVW5dD0IWAGwKG1sVWgAGZF5VDHtUVypcWBhdbTYyV25tOEsnAF9WdVpGWgNSVVgOZhZARCpeQw4WMwYKXVxfC08XB18KGloXXzYy"></div></div>
<p>NotificationCenter是一个系统组件,它负责协调和管理事件的通知和响应。它的基本原理是基于<strong>观察者模式</strong>!而 Apple 对其是闭源的,因此无法查看 NotificationCenter 的源码,但是可以通过分析开源的 Swift 来理解 NotificationCenter 的实现,以下是一个简化的实现:</p>
<h1>简单实现</h1>
<p class="maodian"></p><h2>1、首先定义一个NotificationCenter类定义</h2>
<div class="jb51code"><pre class="brush:java;">class RYNotificationCenter {
private init(){}
static let `default` = RYNotificationCenter()
private var observers: = []
}
</pre></div>
<p>定义了一个单例,用于在整个程序中共享,<code>observers</code>数组用来存储已经注册的所有观察者。</p>
<p class="maodian"></p><h2>2、然后定义一个观察者对象</h2>
<p>观察者对象用来封装具体的观察者的信息。</p>
<div class="jb51code"><pre class="brush:java;">class RYNotificationObserver {
var name: String
var block: (Notification) -> Void
init(name: String, block: @escaping (Notification) -> Void) {
self.name = name
self.block = block
}
}
</pre></div>
<p class="maodian"></p><h2>3、在NotificationCenter中添加注册观察者的方法</h2>
<div class="jb51code"><pre class="brush:java;">
func addObserver(name: String, block: @escaping (Notification) -> Void) -> RYNotificationObserver {
let observer = RYNotificationObserver(name: name, block: block)
observers.append(observer)
return observer
}
</pre></div>
<p><code>addObserver</code>方法用于注册观察者。在这个实现中,我们创建了一个新 <code>RYNotificationObserver</code> 对象并将其添加到 <code>observers</code> 数组。这个方法返回观察者对象,以便稍后从 <code>NotificationCenter</code> 中移除。</p>
<p class="maodian"></p><h2>4、在 NotificationCenter 中添加发送通知的方法</h2>
<div class="jb51code"><pre class="brush:java;">
/// 发送通知的本质是利用了观察者模式
/// 让观察者数组执行闭包中的代码
func post(name: String, userInfo: ? = nil) {
let notification = Notification(name: Notification.Name(name), userInfo: userInfo)
observers
.filter({ $0.name == name })
.forEach { $0.block(notification) }
}
</pre></div>
<p><code>post</code> 方法用来发送通知,它接受通知名以及可选的<code>userInfo</code>字典。同时参数都包装在<code>Notification</code>对象中,然后遍历 <code>observers</code> 数组。如果观察者的名称和通知名称匹配,我们将执行保存的<code>block</code>。</p>
<p class="maodian"></p><h2>5、在NotificationCenter中添加移除通知者的方法</h2>
<div class="jb51code"><pre class="brush:java;">func removeObserver(_ observer: RYNotificationObserver) {
if let index = observers.firstIndex(where: { $0 === observer }) {
observers.remove(at: index)
}
}
</pre></div>
<p><code>removeObserver</code> 方法用于移除观察者。它接受一个观察者对象并从 <code>observers</code> 数组中移除它。</p>
<h1>NotificationCenter的源码分析</h1>
<p>普遍来说,现在分析 <code>NotificationCenter</code> 的源码,一般是 github.com/gnustep/lib… ,这是在 gnustep 库的源码中,它和官方的具体实现肯定是有差异的,但是可以以它为参考的对象,在这里通知的源码使用了三个主要的类:</p>
<ul><li>NSNotification</li><li>NSNotificationCenter</li><li>NSNotificationQueue</li></ul>
<p class="maodian"></p><h3>NSNotificationCenter 实现</h3>
<p>用于在观察者和发送者之间发送通知,这是核心类,它的方法和Objective-C是一致的,使用 <code>**addObserver:selector:name:object:</code> 方法来添加观察者,但是它在内部使用了C语言实现链表的数据结构 <code>Obs</code> 存储观察者相关的信息:</p>
<div class="jb51code"><pre class="brush:java;">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;
</pre></div>
<p>而在 <code>postNotificationName:object:userInfo:</code> 方法执行的时候会通过通知名找到封装好的 <code>Obs</code> 观察者,然后执行相应的方法:</p>
<div class="jb51code"><pre class="brush:java;">- (void) postNotificationName: (NSString*)name
object: (id)object
userInfo: (NSDictionary*)info
{
// 先封装好notification
GSNotification *notification;
notification = (id)NSAllocateObject(concrete, 0, NSDefaultMallocZone());
notification->_name = ];
notification->_object = ;
notification->_info = ;
;
}
// 然后调用观察者的selector方法
- (void) _postAndRealse: (NSNotification*)notification {
......
;
......
}
</pre></div>
<p>当然,要将封装好的 <code>notification</code> ,作为参数传递给观察者需要执行的 <code>selector</code> 。</p>
<p class="maodian"></p><h3>NSNotification 实现</h3>
<p>那么 Notifiation 呢?它是一个包含了通知的名称、发送者对象以及用户信息字典的不可变对象。</p>
<div class="jb51code"><pre class="brush:java;">- (id) initWithCoder: (NSCoder*)aCoder
{
NSString *name;
id object;
NSDictionary *info;
id n;
;
;
;
n = ;
RELEASE(name);
RELEASE(object);
RELEASE(info);
DESTROY(self);
return RETAIN(n);
}
</pre></div>
<p class="maodian"></p><h3>NSNotificationQueue 的实现</h3>
<p>最后是 NSNotificationQueue 的实现,它是一个用于管理通知发送的队列,可以按照特定的发送模式(例如合并相同的通知或按发送顺序)将通知排队。</p>
<div class="jb51code"><pre class="brush:java;">- (void) enqueueNotification: (NSNotification*)notification postingStyle:(NSPostingStyle)postingStyle coalesceMask: (NSUInteger)coalesceMask forModes: (NSArray*)modes
{
if (modes == nil)
{
modes = defaultMode;
}
if (coalesceMask != NSNotificationNoCoalescing)
{
;
}
switch (postingStyle) {
case NSPostNow: {
NSString *mode;
mode = [ currentMode];
if (mode == nil || != NSNotFound)
{
;
}
}
break;
case NSPostASAP:
add_to_queue(_asapQueue, notification, modes, _zone);
break;
case NSPostWhenIdle:
add_to_queue(_idleQueue, notification, modes, _zone);
break;
}
}
</pre></div>
<p>当使用 <code>NSNotificationQueue</code> 的时候,就不需要我们手动发送 Notification 了,<code>NSNotificationQueue</code> 会自动帮我们发送,在上述代码中,如果是 <code>NSPostNow</code>,那么通知会立马被发送,否则就先加入队列中:<code>_asapQueue</code> 或者 <code>_idleQueue</code> ,然后在合适的时候执行队列中的通知,比如:</p>
<div class="jb51code"><pre class="brush:java;">
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);
}
}
}
</pre></div>
<h1>问题:如果<code>NotificationCenter</code> 添加的观察者是self,会造成循环引用吗?</h1>
<p>答案是:<strong>不会!</strong></p>
<p>NotificationCenter 对观察者的引用方式是弱引用(weak),而不是强持有(strong)。因此,当一个对象被销毁时,它的 <code>deinit</code> 方法会被调用,即使它是一个观察者。所以即使我们不在 <code>deinit</code> 方法中添加移除 self 的操作也是可以的,因为 NotificationCenter 并没有对观察者强持有。</p>
<h1>问题:如果 <code>NotificationCenter</code> 添加的是 block ,而 block 强持有了 self ,这会造成循环引用吗?</h1>
<p>答案是:<strong>会!</strong></p>
<p>从iOS 9开始,如果使用了基于 block 的观察者,那么就需要去小心观察者的生命周期了,因为<code>NotificationCenter</code> 对添加的 block 是强持有的,正如上述简单实现中的那样,它对闭包中捕获的变量就也是强持有的,所以为了避免这种现象,需要确保使用 <code></code> 来捕获列表。</p>
<p>在实际使用的时候,由于编码惯性,可能会在 <code>deinit</code> 方法中移除基于 block 的观察者以解决该问题:</p>
<div class="jb51code"><pre class="brush:java;">
class ViewController: UIViewController {
private weak var observer: NSObjectProtocol!
func addObserver() {
observer = NotificationCenter.default.addObserver(forName: NSNotification.Name("test"), object: nil, queue: OperationQueue.main) { _ in
self.view.backgroundColor = UIColor.white
}
}
deinit {
NotificationCenter.default.removeObserver(observer!)
}
}
</pre></div>
<p><strong>但是在这种情况下,</strong> <code>deinit</code> <strong>方法并不会执行!</strong> 原因就是 <code>NotificationCenter</code> 持有了 block, 也间接持有了 self,而 <code>NotificationCenter</code> 是一个单例,所以这种持有关系是一直存在的,导致了 <code>deinit</code> 方法并不会执行!</p>
<h1>问题:观察者的 <code>selector</code> 执行的线程和发送通知的线程有关吗?</h1>
<p><strong>答案是:正相关!</strong></p>
<p>从上文中的简单实现以及GNU的源码中基本可以看出结论了。添加观察者的线程并没有什么影响,<strong>而发送通知的线程,其实就是调用方法执行的线程,所以两者是在同一线程执行的。</strong></p>
<div class="jb51code"><pre class="brush:java;">func addObserver() {
NotificationCenter.default.addObserver(self, selector: #selector(click), name: NSNotification.Name.init("test"), object: nil)
DispatchQueue.global().async {
NotificationCenter.default.post(name: NSNotification.Name.init("test"), object: nil)
NSLog("curretThread1: \(Thread.current)")
}
}
@objc func click() {
NSLog("curretThread2: \(Thread.current)")
}
// curretThread2: <NSThread: 0x600001358240>{number = 6, name = (null)}
// curretThread1: <NSThread: 0x600001358240>{number = 6, name = (null)}
</pre></div>
<p>同时还需要注意的就是通知发送,然后 <code>selector</code> 被执行,这个过程其实本质上是一个观察者模式的实现方式,同时,它也是同步执行的,再执行完发送消息的方法后就会去寻找对应的 <code>Observer</code> ,找到之后就执行相应的 <code>selector</code> ,执行完之后,发送消息的方法才执行完毕了。</p>
<p><strong>所以发送通知和监听通知执行方法的核心是:相同线程执行 且 同步执行。</strong></p>
<p>以上就是NotificationCenter类实现原理的详细内容,更多关于NotificationCenter 原理的资料请关注琼殿技术社区其它相关文章!</p>
<div class="art_xg">
<b>您可能感兴趣的文章:</b><ul><li>iOS NSNotificationCenter通知中心使用小结</li><li>ios NSNotificationCenter通知的简单使用</li><li>Swift中通知中心(NotificationCenter)的使用示例</li></ul>
</div>
</div>
<!--endmain-->
頁:
[1]