首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

iOS KVO 機制底層原理:沒有「監聽者模式」,只有「觀察者模式」與 ISA 混寫

     編輯:程式設計綜合問答

面試官聽想要的答案

**KVO (Key-Value Observing) 核心使用的是觀察者模式**,而非「監聽者模式」(Listener Pattern)——兩者在 GoF 分類中本質相同,但 iOS 官方文檔、運行時源碼統一術語為 **Observer Pattern**。

KVO 實現的三大支柱

1. ISA 混寫 —— 最核心黑魔法

  • 對象被觀察時,Runtime 動態創建一個**中間類**(名稱如 `NSKVONotifying_OriginalClass`)
  • 修改對象的 `isa` 指針指向該中間類,**原類保持不變**
  • 中間類**重寫被觀察屬性的 setter**,內部調用:
    `willChangeValueForKey:` → `super.setter` → `didChangeValueForKey:`

2. 依賴鍵路徑自動推導

// 觀察 "person.address.city"
[person addObserver:self forKeyPath:@"address.city" options:0 context:nil];
// 修改 city 時,Runtime 自動觸發 person 的 address.city 變更通知
// 依賴鍵由 +keyPathsForValuesAffecting 決定

3. 觀察者註冊表

系統維护一個全局映射:
`ObservedObject → (KeyPath → [ObserverInfo])`
`ObserverInfo` 包含:觀察者對象、選項、上下文指針、塊回調(iOS 13+ 支持 block 觀察)。

關鍵源碼路徑(開源 objc4)

  • `NSObject.mm` → `addObserver:forKeyPath:options:context:`
  • `NSKVONotifying.m` → 動態生成的中間類實現
  • `NSKeyValueObserving.m` → 通知分發邏輯

常見坑點與最佳實踐

坑點後果規避
未移除觀察者就 deallocCrash:`NSInternalInconsistencyException`deinit / dealloc 中 `removeObserver:forKeyPath:`
重複添加同一觀察者多次回調、移除困難保持弱引用、統一管理註冊
觀察非 @objc / dynamic 屬性Swift 類屬性需加 @objc dynamicSwift 類繼承 NSObject 或標記 @objc dynamic
在 observeValueForKeyPath 中修改同屬性無限遞歸、棧溢出標記標誌位、異步處理或用 KVC 繞過

KVO vs NotificationCenter vs Combine

維度KVONotificationCenterCombine
耦合度低(僅需 KeyPath 字符串)低(字符串名)極低(類型安全 Publisher)
性能中(Runtime 攔截 setter)高(異步分發)高(優化過的流式處理)
適用場景屬性級別變更監聽跨模塊事件廣播現代響應式編程、異步鏈
線程安全同線程同步回調可指定隊列可指定調度器

面試加分話術

「KVO 底層通過 **ISA 混寫** 動態生成中間類重寫 setter,在 `willChangeValueForKey:` / `didChangeValueForKey:` 包裹原始賦值,實現屬性變更的自動感知。這是 **觀察者模式** 在 Runtime 層的極致體現。相比 NotificationCenter,KVO 更適合**細粒度屬性綁定**;但現代 Swift 開發首選 **Combine** 或 **Swift Observation (iOS 17+)**,獲得編譯期類型安全與結構化並發支持。」

總結

KVO 使用**觀察者模式**,核心技術是 **ISA 混寫 + setter 攔截 + 依賴鍵路徑**。面試時說清「動態生成中間類、修改 isa、重寫 setter、分發通知」四步走,再對比 Combine/NotificationCenter,即可展示對 iOS 運行時機制的深度理解。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved