NotificationCenter类实现原理
作者:向辉_
正文
NotificationCenter是一个系统组件,它负责协调和管理事件的通知和响应。它的基本原理是基于观察者模式!而 Apple 对其是闭源的,因此无法查看 NotificationCenter 的源码,但是可以通过分析开源的 Swift 来理解 NotificationCenter 的实现,以下是一个简化的实现:
简单实现
1、首先定义一个NotificationCenter类定义
class RYNotificationCenter { private init(){} static let `default` = RYNotificationCenter() private var observers: [RYNotificationObserver] = [] }
定义了一个单例,用于在整个程序中共享,observers
数组用来存储已经注册的所有观察者。
2、然后定义一个观察者对象
观察者对象用来封装具体的观察者的信息。
class RYNotificationObserver { var name: String var block: (Notification) -> Void init(name: String, block: @escaping (Notification) -> Void) { self.name = name self.block = block } }
3、在NotificationCenter中添加注册观察者的方法
func addObserver(name: String, block: @escaping (Notification) -> Void) -> RYNotificationObserver { let observer = RYNotificationObserver(name: name, block: block) observers.append(observer) return observer }
addObserver
方法用于注册观察者。在这个实现中,我们创建了一个新 RYNotificationObserver
对象并将其添加到 observers
数组。这个方法返回观察者对象,以便稍后从 NotificationCenter
中移除。
4、在 NotificationCenter 中添加发送通知的方法
/// 发送通知的本质是利用了观察者模式 /// 让观察者数组执行闭包中的代码 func post(name: String, userInfo: [AnyHashable: Any]? = nil) { let notification = Notification(name: Notification.Name(name), userInfo: userInfo) observers .filter({ $0.name == name }) .forEach { $0.block(notification) } }
post
方法用来发送通知,它接受通知名以及可选的userInfo
字典。同时参数都包装在Notification
对象中,然后遍历 observers
数组。如果观察者的名称和通知名称匹配,我们将执行保存的block
。
5、在NotificationCenter中添加移除通知者的方法
func removeObserver(_ observer: RYNotificationObserver) { if let index = observers.firstIndex(where: { $0 === observer }) { observers.remove(at: index) } }
removeObserver
方法用于移除观察者。它接受一个观察者对象并从 observers
数组中移除它。
NotificationCenter的源码分析
普遍来说,现在分析 NotificationCenter
的源码,一般是 github.com/gnustep/lib… ,这是在 gnustep 库的源码中,它和官方的具体实现肯定是有差异的,但是可以以它为参考的对象,在这里通知的源码使用了三个主要的类:
- NSNotification
- NSNotificationCenter
- NSNotificationQueue
NSNotificationCenter 实现
用于在观察者和发送者之间发送通知,这是核心类,它的方法和Objective-C是一致的,使用 **addObserver:selector:name:object:
方法来添加观察者,但是它在内部使用了C语言实现链表的数据结构 Obs
存储观察者相关的信息:
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;
而在 postNotificationName:object:userInfo:
方法执行的时候会通过通知名找到封装好的 Obs
观察者,然后执行相应的方法:
- (void) postNotificationName: (NSString*)name object: (id)object userInfo: (NSDictionary*)info { // 先封装好notification 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]; } // 然后调用观察者的selector方法 - (void) _postAndRealse: (NSNotification*)notification { ...... [o->observer performSelector: o->selector withObject: notification]; ...... }
当然,要将封装好的 notification
,作为参数传递给观察者需要执行的 selector
。
NSNotification 实现
那么 Notifiation 呢?它是一个包含了通知的名称、发送者对象以及用户信息字典的不可变对象。
- (id) initWithCoder: (NSCoder*)aCoder { NSString *name; id object; NSDictionary *info; id n; [aCoder decodeValueOfObjCType: @encode(id) at: &name]; [aCoder decodeValueOfObjCType: @encode(id) at: &object]; [aCoder decodeValueOfObjCType: @encode(id) at: &info]; n = [NSNotification notificationWithName: name object: object userInfo: info]; RELEASE(name); RELEASE(object); RELEASE(info); DESTROY(self); return RETAIN(n); }
NSNotificationQueue 的实现
最后是 NSNotificationQueue 的实现,它是一个用于管理通知发送的队列,可以按照特定的发送模式(例如合并相同的通知或按发送顺序)将通知排队。
- (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; } }
当使用 NSNotificationQueue
的时候,就不需要我们手动发送 Notification 了,NSNotificationQueue
会自动帮我们发送,在上述代码中,如果是 NSPostNow
,那么通知会立马被发送,否则就先加入队列中:_asapQueue
或者 _idleQueue
,然后在合适的时候执行队列中的通知,比如:
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); } } }
问题:如果NotificationCenter
添加的观察者是self,会造成循环引用吗?
答案是:不会!
NotificationCenter 对观察者的引用方式是弱引用(weak),而不是强持有(strong)。因此,当一个对象被销毁时,它的 deinit
方法会被调用,即使它是一个观察者。所以即使我们不在 deinit
方法中添加移除 self 的操作也是可以的,因为 NotificationCenter 并没有对观察者强持有。
问题:如果 NotificationCenter
添加的是 block ,而 block 强持有了 self ,这会造成循环引用吗?
答案是:会!
从iOS 9开始,如果使用了基于 block 的观察者,那么就需要去小心观察者的生命周期了,因为NotificationCenter
对添加的 block 是强持有的,正如上述简单实现中的那样,它对闭包中捕获的变量就也是强持有的,所以为了避免这种现象,需要确保使用 [weak self]
来捕获列表。
在实际使用的时候,由于编码惯性,可能会在 deinit
方法中移除基于 block 的观察者以解决该问题:
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!) } }
但是在这种情况下, deinit
方法并不会执行! 原因就是 NotificationCenter
持有了 block, 也间接持有了 self,而 NotificationCenter
是一个单例,所以这种持有关系是一直存在的,导致了 deinit
方法并不会执行!
问题:观察者的 selector
执行的线程和发送通知的线程有关吗?
答案是:正相关!
从上文中的简单实现以及GNU的源码中基本可以看出结论了。添加观察者的线程并没有什么影响,而发送通知的线程,其实就是调用方法执行的线程,所以两者是在同一线程执行的。
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)}
同时还需要注意的就是通知发送,然后 selector
被执行,这个过程其实本质上是一个观察者模式的实现方式,同时,它也是同步执行的,再执行完发送消息的方法后就会去寻找对应的 Observer
,找到之后就执行相应的 selector
,执行完之后,发送消息的方法才执行完毕了。
所以发送通知和监听通知执行方法的核心是:相同线程执行 且 同步执行。
以上就是NotificationCenter类实现原理的详细内容,更多关于NotificationCenter 原理的资料请关注脚本之家其它相关文章!