最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

NotificationCenter類實(shí)現(xiàn)原理

 更新時(shí)間:2023年03月31日 08:43:00   作者:向輝_  
這篇文章主要為大家介紹了NotificationCenter類實(shí)現(xiàn)原理源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

NotificationCenter是一個(gè)系統(tǒng)組件,它負(fù)責(zé)協(xié)調(diào)和管理事件的通知和響應(yīng)。它的基本原理是基于觀察者模式!而 Apple 對(duì)其是閉源的,因此無(wú)法查看 NotificationCenter 的源碼,但是可以通過分析開源的 Swift 來(lái)理解 NotificationCenter 的實(shí)現(xiàn),以下是一個(gè)簡(jiǎn)化的實(shí)現(xiàn):

簡(jiǎn)單實(shí)現(xiàn)

1、首先定義一個(gè)NotificationCenter類定義

class RYNotificationCenter {
    private init(){}
    static let `default` = RYNotificationCenter()
    private var observers: [RYNotificationObserver] = []
}

定義了一個(gè)單例,用于在整個(gè)程序中共享,observers數(shù)組用來(lái)存儲(chǔ)已經(jīng)注冊(cè)的所有觀察者。

2、然后定義一個(gè)觀察者對(duì)象

觀察者對(duì)象用來(lái)封裝具體的觀察者的信息。

class RYNotificationObserver {
    var name: String
    var block: (Notification) -> Void
    init(name: String, block: @escaping (Notification) -> Void) {
        self.name = name
        self.block = block
    }
}

3、在NotificationCenter中添加注冊(cè)觀察者的方法

func addObserver(name: String, block: @escaping (Notification) -> Void) -> RYNotificationObserver {
    let observer = RYNotificationObserver(name: name, block: block)
    observers.append(observer)
    return observer
}

addObserver方法用于注冊(cè)觀察者。在這個(gè)實(shí)現(xiàn)中,我們創(chuàng)建了一個(gè)新 RYNotificationObserver 對(duì)象并將其添加到 observers 數(shù)組。這個(gè)方法返回觀察者對(duì)象,以便稍后從 NotificationCenter 中移除。

4、在 NotificationCenter 中添加發(fā)送通知的方法

/// 發(fā)送通知的本質(zhì)是利用了觀察者模式
/// 讓觀察者數(shù)組執(zhí)行閉包中的代碼
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 方法用來(lái)發(fā)送通知,它接受通知名以及可選的userInfo字典。同時(shí)參數(shù)都包裝在Notification對(duì)象中,然后遍歷 observers 數(shù)組。如果觀察者的名稱和通知名稱匹配,我們將執(zhí)行保存的block。

5、在NotificationCenter中添加移除通知者的方法

func removeObserver(_ observer: RYNotificationObserver) {
    if let index = observers.firstIndex(where: { $0 === observer }) {
        observers.remove(at: index)
    }
}

removeObserver 方法用于移除觀察者。它接受一個(gè)觀察者對(duì)象并從 observers 數(shù)組中移除它。

NotificationCenter的源碼分析

普遍來(lái)說(shuō),現(xiàn)在分析 NotificationCenter 的源碼,一般是 github.com/gnustep/lib… ,這是在 gnustep 庫(kù)的源碼中,它和官方的具體實(shí)現(xiàn)肯定是有差異的,但是可以以它為參考的對(duì)象,在這里通知的源碼使用了三個(gè)主要的類:

  • NSNotification
  • NSNotificationCenter
  • NSNotificationQueue

NSNotificationCenter 實(shí)現(xiàn)

用于在觀察者和發(fā)送者之間發(fā)送通知,這是核心類,它的方法和Objective-C是一致的,使用 **addObserver:selector:name:object: 方法來(lái)添加觀察者,但是它在內(nèi)部使用了C語(yǔ)言實(shí)現(xiàn)鏈表的數(shù)據(jù)結(jié)構(gòu) Obs 存儲(chǔ)觀察者相關(guān)的信息:

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: 方法執(zhí)行的時(shí)候會(huì)通過通知名找到封裝好的 Obs 觀察者,然后執(zhí)行相應(yīng)的方法:

- (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];
}
// 然后調(diào)用觀察者的selector方法
- (void) _postAndRealse: (NSNotification*)notification {
......
[o->observer performSelector: o->selector withObject: notification];
......
}

當(dāng)然,要將封裝好的 notification ,作為參數(shù)傳遞給觀察者需要執(zhí)行的 selector

NSNotification 實(shí)現(xiàn)

那么 Notifiation 呢?它是一個(gè)包含了通知的名稱、發(fā)送者對(duì)象以及用戶信息字典的不可變對(duì)象。

- (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 的實(shí)現(xiàn)

最后是 NSNotificationQueue 的實(shí)現(xiàn),它是一個(gè)用于管理通知發(fā)送的隊(duì)列,可以按照特定的發(fā)送模式(例如合并相同的通知或按發(fā)送順序)將通知排隊(duì)。

- (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;
    }
}

當(dāng)使用 NSNotificationQueue 的時(shí)候,就不需要我們手動(dòng)發(fā)送 Notification 了,NSNotificationQueue 會(huì)自動(dòng)幫我們發(fā)送,在上述代碼中,如果是 NSPostNow,那么通知會(huì)立馬被發(fā)送,否則就先加入隊(duì)列中:_asapQueue 或者 _idleQueue ,然后在合適的時(shí)候執(zhí)行隊(duì)列中的通知,比如:

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,會(huì)造成循環(huán)引用嗎?

答案是:不會(huì)!

NotificationCenter 對(duì)觀察者的引用方式是弱引用(weak),而不是強(qiáng)持有(strong)。因此,當(dāng)一個(gè)對(duì)象被銷毀時(shí),它的 deinit 方法會(huì)被調(diào)用,即使它是一個(gè)觀察者。所以即使我們不在 deinit 方法中添加移除 self 的操作也是可以的,因?yàn)?NotificationCenter 并沒有對(duì)觀察者強(qiáng)持有。

問題:如果 NotificationCenter 添加的是 block ,而 block 強(qiáng)持有了 self ,這會(huì)造成循環(huán)引用嗎?

答案是:會(huì)!

從iOS 9開始,如果使用了基于 block 的觀察者,那么就需要去小心觀察者的生命周期了,因?yàn)?code>NotificationCenter 對(duì)添加的 block 是強(qiáng)持有的,正如上述簡(jiǎn)單實(shí)現(xiàn)中的那樣,它對(duì)閉包中捕獲的變量就也是強(qiáng)持有的,所以為了避免這種現(xiàn)象,需要確保使用 [weak self] 來(lái)捕獲列表。

在實(shí)際使用的時(shí)候,由于編碼慣性,可能會(huì)在 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 方法并不會(huì)執(zhí)行! 原因就是 NotificationCenter 持有了 block, 也間接持有了 self,而 NotificationCenter 是一個(gè)單例,所以這種持有關(guān)系是一直存在的,導(dǎo)致了 deinit 方法并不會(huì)執(zhí)行!

問題:觀察者的 selector 執(zhí)行的線程和發(fā)送通知的線程有關(guān)嗎?

答案是:正相關(guān)!

從上文中的簡(jiǎn)單實(shí)現(xiàn)以及GNU的源碼中基本可以看出結(jié)論了。添加觀察者的線程并沒有什么影響,而發(fā)送通知的線程,其實(shí)就是調(diào)用方法執(zhí)行的線程,所以兩者是在同一線程執(zhí)行的。

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)}

同時(shí)還需要注意的就是通知發(fā)送,然后 selector 被執(zhí)行,這個(gè)過程其實(shí)本質(zhì)上是一個(gè)觀察者模式的實(shí)現(xiàn)方式,同時(shí),它也是同步執(zhí)行的,再執(zhí)行完發(fā)送消息的方法后就會(huì)去尋找對(duì)應(yīng)的 Observer ,找到之后就執(zhí)行相應(yīng)的 selector ,執(zhí)行完之后,發(fā)送消息的方法才執(zhí)行完畢了。

所以發(fā)送通知和監(jiān)聽通知執(zhí)行方法的核心是:相同線程執(zhí)行 且 同步執(zhí)行。

以上就是NotificationCenter類實(shí)現(xiàn)原理的詳細(xì)內(nèi)容,更多關(guān)于NotificationCenter 原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • iOS Swift UICollectionView橫向分頁(yè)滾動(dòng),cell左右排版問題詳解

    iOS Swift UICollectionView橫向分頁(yè)滾動(dòng),cell左右排版問題詳解

    UICollectionView是iOS中比較常見的一個(gè)控件,這篇文章主要給大家介紹了關(guān)于iOS Swift UICollectionView橫向分頁(yè)滾動(dòng),cell左右排版問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-12-12
  • 分析Swift性能高效的原因

    分析Swift性能高效的原因

    絕大多數(shù)公司選擇Swift語(yǔ)言開發(fā)iOS應(yīng)用,主要原因是因?yàn)镾wift相比Objc有更快的運(yùn)行效率,更加安全的類型檢測(cè),更多現(xiàn)代語(yǔ)言的特性提升開發(fā)效率;這一系列的優(yōu)點(diǎn)使Swift語(yǔ)言的熱度越來(lái)越高。
    2020-10-10
  • Swift教程之集合類型詳解

    Swift教程之集合類型詳解

    這篇文章主要介紹了Swift教程之集合類型詳解,Swift 提供兩種集合類型來(lái)存儲(chǔ)集合,數(shù)組和字典,本文詳細(xì)講解了數(shù)組的創(chuàng)建、讀取和修改數(shù)組、遍歷數(shù)組以及集合的操作等內(nèi)容,需要的朋友可以參考下
    2015-01-01
  • iOS開發(fā)中Swift 指紋驗(yàn)證功能模塊實(shí)例代碼

    iOS開發(fā)中Swift 指紋驗(yàn)證功能模塊實(shí)例代碼

    本文給大家分享ios調(diào)用touchid代碼塊,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下把
    2017-03-03
  • Swift實(shí)現(xiàn)堆排序算法的代碼示例

    Swift實(shí)現(xiàn)堆排序算法的代碼示例

    堆排序(HeapSort)是一樹形選擇排序,堆排序的時(shí)間復(fù)雜度O(nlogn),這里我們來(lái)看一下Swift實(shí)現(xiàn)基堆排序算法的代碼示例,首先對(duì)堆排序算法的基本概念作一個(gè)了解:
    2016-06-06
  • 舉例講解Swift編程中switch...case語(yǔ)句的用法

    舉例講解Swift編程中switch...case語(yǔ)句的用法

    這篇文章主要介紹了Swift編程中switch...case語(yǔ)句的用法,其中fallthrough關(guān)鍵字在switch語(yǔ)句中的使用是重點(diǎn),需要的朋友可以參考下
    2016-04-04
  • swiftui開發(fā)之padding默認(rèn)值設(shè)置詳解

    swiftui開發(fā)之padding默認(rèn)值設(shè)置詳解

    這篇文章主要為大家介紹了swiftui開發(fā)之padding默認(rèn)值設(shè)置詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • Swift TableView實(shí)現(xiàn)凍結(jié)窗格功能

    Swift TableView實(shí)現(xiàn)凍結(jié)窗格功能

    這篇文章主要為大家詳細(xì)介紹了Swift TableView實(shí)現(xiàn)凍結(jié)窗格功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • 如何快速用上Swift靜態(tài)庫(kù)詳解

    如何快速用上Swift靜態(tài)庫(kù)詳解

    這篇文章主要給大家介紹了關(guān)于如何快速用上Swift靜態(tài)庫(kù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-03-03
  • Swift 中如何使用 Option Pattern 改善可選項(xiàng)的 API 設(shè)計(jì)

    Swift 中如何使用 Option Pattern 改善可選項(xiàng)的 API 設(shè)計(jì)

    這篇文章主要介紹了Swift 中如何使用 Option Pattern 改善可選項(xiàng)的 API 設(shè)計(jì),幫助大家更好的進(jìn)行ios開發(fā),感興趣的朋友可以了解下
    2020-10-10

最新評(píng)論

屏南县| 瑞金市| 长海县| 涟源市| 新建县| 泽库县| 布拖县| 皋兰县| 文水县| 朝阳区| 镇沅| 济源市| 九龙城区| 长阳| 宁明县| 大同县| 灵山县| 黄梅县| 旬阳县| 祁阳县| 汨罗市| 金沙县| 福清市| 昌乐县| 普陀区| 临武县| 兰州市| 思茅市| 轮台县| 安吉县| 牙克石市| 磐安县| 兰溪市| 蕲春县| 金山区| 锡林郭勒盟| 鄢陵县| 洛宁县| 松阳县| 田东县| 沅陵县|