您好,登錄后才能下訂單哦!
這篇文章主要講解了“NotificationCenter類的實現原理是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“NotificationCenter類的實現原理是什么”吧!
NotificationCenter是一個系統組件,它負責協調和管理事件的通知和響應。它的基本原理是基于觀察者模式!而 Apple 對其是閉源的,因此無法查看 NotificationCenter 的源碼,但是可以通過分析開源的 Swift 來理解 NotificationCenter 的實現,以下是一個簡化的實現:
class RYNotificationCenter { private init(){} static let `default` = RYNotificationCenter() private var observers: [RYNotificationObserver] = [] }
定義了一個單例,用于在整個程序中共享,observers
數組用來存儲已經注冊的所有觀察者。
觀察者對象用來封裝具體的觀察者的信息。
class RYNotificationObserver { var name: String var block: (Notification) -> Void init(name: String, block: @escaping (Notification) -> Void) { self.name = name self.block = block } }
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
中移除。
/// 發送通知的本質是利用了觀察者模式 /// 讓觀察者數組執行閉包中的代碼 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
。
func removeObserver(_ observer: RYNotificationObserver) { if let index = observers.firstIndex(where: { $0 === observer }) { observers.remove(at: index) } }
removeObserver
方法用于移除觀察者。它接受一個觀察者對象并從 observers
數組中移除它。
普遍來說,現在分析 NotificationCenter
的源碼,一般是 github.com/gnustep/lib… ,這是在 gnustep 庫的源碼中,它和官方的具體實現肯定是有差異的,但是可以以它為參考的對象,在這里通知的源碼使用了三個主要的類:
NSNotification
NSNotificationCenter
NSNotificationQueue
用于在觀察者和發送者之間發送通知,這是核心類,它的方法和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
。
那么 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 的實現,它是一個用于管理通知發送的隊列,可以按照特定的發送模式(例如合并相同的通知或按發送順序)將通知排隊。
- (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類的實現原理是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。