iOS開發中的KVO以及原理

KVO概述

????????KVO(Key-Value-Observing)是iOS開發中一種觀察者模式實現,允許對象監聽另一個對象屬性的變化。當被觀察屬性的值發生變化時,觀察者會收到通知。KVO基于NSKeyValueObserving協議實現,是Foundation框架的核心功能之一。

1.KVO的基本使用

? ? ? ? 我們以下面的demo為例,當我們滑動列表的時候,頁面上顯示UITableView的偏移量。

圖1.KVO監聽UITableView滑動時候的偏移量

????????具體的使用步驟如下:

1.添加觀察者

????????通過addObserver:forKeyPath:options:context:方法注冊觀察者。

observedObject.addObserver(observer, forKeyPath: "propertyName", options: [.new, .old], context: nil
)

????????options:指定接收變化前的值(.old)或變化后的值(.new)。

????????context:區分不同觀察事件的上下文指針(通常用nil)。

2.實現回調方法

????????觀察者需重寫observeValue(forKeyPath:of:change:context:)方法處理通知:

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?
) {if keyPath == "propertyName" {let oldValue = change?[.oldKey]let newValue = change?[.newKey]// 處理值變化}
}

3.移除觀察者

? ? ? ? 在觀察者銷毀前必須調用removeObserver:forKeyPath:

observedObject.removeObserver(observer, forKeyPath: "propertyName")

4.完整的代碼

? ? ? ? 完整的代碼如下:

import UIKit
import IFLYCommonKit
import SnapKitclass IFLYKVODemosVC: IFLYCommonBaseVC {private let tableView = UITableView()private let offsetLabel: UILabel = {let label = UILabel()label.backgroundColor = UIColor.black.withAlphaComponent(0.6)label.textColor = .whitelabel.font = UIFont.systemFont(ofSize: 14)label.textAlignment = .centerlabel.layer.cornerRadius = 5label.clipsToBounds = truereturn label}()private var contentOffsetObservation: NSKeyValueObservation?override func viewDidLoad() {super.viewDidLoad()title = "KVO原理"// Do any additional setup after loading the view.view.addSubview(self.tableView)view.addSubview(offsetLabel)tableView.snp.makeConstraints { make inmake.edges.equalToSuperview()}offsetLabel.snp.makeConstraints { make inmake.center.equalToSuperview()make.height.equalTo(30)make.width.equalTo(180)}contentOffsetObservation = tableView.observe(\.contentOffset, options: [.new]) { [weak self] tableView, change inguard let self = self, let newOffset = change.newValue else { return }let offsetY = newOffset.yself.offsetLabel.text = String(format: "Offset Y: %.2f", offsetY)self.offsetLabel.isHidden = falseNSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(self.hideOffsetLabel), object: nil)self.perform(#selector(self.hideOffsetLabel), with: nil, afterDelay: 0.5)}}@objc private func hideOffsetLabel() {offsetLabel.isHidden = true}deinit {contentOffsetObservation?.invalidate()}override func viewWillAppear(_ animated: Bool) {super.viewWillAppear(animated)tableView.dataSource = selftableView.delegate = selftableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")}}extension IFLYKVODemosVC: UITableViewDataSource, UITableViewDelegate {func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {return 20}func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)cell.textLabel?.text = "Row \(indexPath.row)"return cell}func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {tableView.deselectRow(at: indexPath, animated: true)print("Selected row \(indexPath.row)")}
}

2.KVO的實現原理

1.動態子類(Dynamic Subclassing)
????????KVO會在運行時動態生成被觀察類的子類(命名規則為NSKVONotifying_ClassName),并重寫被觀察屬性的setter方法。

2.Setter方法重寫
????????動態子類的setter方法會調用以下流程:

  1. willChangeValueForKey::通知系統屬性即將變化。
  2. 調用原始setter方法修改屬性值。
  3. didChangeValueForKey::通知系統屬性已變化,觸發觀察者回調。

3.手動觸發KVO
????????即使未直接賦值,手動調用willChangeValueForKey:didChangeValueForKey:也能觸發通知:

observedObject.willChangeValue(forKey: "propertyName")
// 手動修改屬性值(如直接操作實例變量)
observedObject.didChangeValue(forKey: "propertyName")

4.isa-swizzling

????????動態子類會修改被觀察對象的isa指針,指向新生成的子類,從而讓方法調用路由到重寫的邏輯。

3.KVO的注意事項

1.移除觀察者

????????未移除觀察者會導致 crash。觀察者銷毀前需確保移除所有觀察。

2.線程安全

????????KVO通知與屬性修改在同一線程觸發,跨線程觀察需謹慎處理線程同步。

3.性能優化

????????避免高頻屬性(如滾動時的UI坐標)使用KVO,頻繁通知可能影響性能。

4.Swift中的使用

????????在Swift中需將觀察對象和屬性標記為@objc dynamic

@objc class MyClass: NSObject {@objc dynamic var value: Int = 0
}

4.KVO的替代方案

  • Combine框架:適用于Swift項目,提供更現代的響應式編程支持。
  • Delegate模式:適合一對一的屬性監聽場景。
  • Property Observers(Swift):直接使用didSetwillSet監聽屬性變化。

通過理解KVO的原理和注意事項,可以更高效地實現數據變化的監聽與響應。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/86516.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/86516.shtml
英文地址,請注明出處:http://en.pswp.cn/web/86516.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

雷卯針對靈眸科技EASY Orin-nano RK3516 開發板防雷防靜電方案

一、應用場景 1. 人臉檢測 2. 人臉識別 3. 安全帽檢測 4. 人員檢測 5. OCR文字識別 6. 人頭檢測 7. 表情神態識別 8. 人體骨骼點識別 9. 火焰檢測 10. 人臉姿態估計 11. 人手檢測 12. 車輛檢測 13. 二維碼識別 二、 功能概述 1 CPU:八核64位ARM v8處…

中國雙非高校經費TOP榜數據分析

當我們習慣性仰望985、211這些“國家隊”時,一批地方重點支持的高校正悄悄發力,手握重金,展現出不遜于名校的“鈔能力”。特別是“雙非”大學中的佼佼者,它們的年度經費預算,足以讓許多普通院校望塵莫及。 今天就帶大…

C++ Lambda表達式詳解:從入門到精通

Lambda表達式是C11引入的最重要特性之一,它徹底改變了我們在C中編寫函數對象的方式。本文將帶你全面掌握Lambda表達式的使用技巧! 1. 什么是Lambda表達式? Lambda表達式是C11引入的一種匿名函數對象,它允許我們在需要函數的地方…

實體類id字段選擇Integer還是Long?

Java實體類ID類型選擇:Integer vs Long 深度解析與最佳實踐 在Java實體類設計中,ID字段的類型選擇看似簡單,卻直接影響系統擴展性、性能和數據一致性。本文將深入探討Integer和Long兩種主鍵類型的差異,并通過實際案例展示如何做出…

變現與自我提升:加法與乘法的智慧抉擇

在當今這個快速發展的時代,無論是追求財富的變現,還是致力于個人能力的提升,我們都會面臨一個關鍵問題:是分類分步地逐步實現,還是將多種要素混合在一起?是簡單地做加法,還是復雜的乘法運算&…

鴻蒙 SideBarContainer 開發攻略:側邊欄交互設計與多端適配

一、引言:側邊欄布局的核心組件 在鴻蒙應用開發中,SideBarContainer 作為構建高效交互界面的核心組件,為開發者提供了靈活的側邊欄布局解決方案。該組件通過標準化的接口設計,實現了側邊欄與內容區的協同展示,適用于文…

Windows系統克隆硬盤后顯示容量與實際容量嚴重不符如何處理?

在 Windows 系統中,克隆硬盤后出現硬盤顯示容量與實際容量不符的問題,通常與分區布局、文件系統未正確調整或克隆工具設置有關。以下是可能的原因及對應的處理方案。 1. 問題原因分析 1.1 分區未正確調整 現象: 克隆后硬盤的總容量未正確顯…

EXCEL數據報表

客單價成交金額*成交客戶數 —— 提取年份 YEAR() 視圖-窗口-新建窗口,就能將excel的一個子表格單拎出來成為獨立窗口,方便對比查看 數據報表的單元格盡量都用公式來填補,鏈接到源表上去。這樣當源表有新數據更新進來后,報表也…

TCP/IP協議簡要概述

一、TCP/IP協議概述 (一)定義 TCP/IP(Transmission Control Protocol/Internet Protocol)協議是一組用于互聯網以及類似計算機網絡的通信協議。它是由網絡層的IP協議和傳輸層的TCP協議組成,但整個TCP/IP協議族包含很…

ubuntu下利用Qt添加相機設備并運行arm程序

一、編譯x86-64平臺的opencv demo 緊接上一篇,我電腦里現在同時存在兩個版本的opencv庫,一個是基于x86-64平臺的3.4.11庫,一個是基于arm平臺的4.7.0庫,現在我正常運行opencv的demo,直接報錯:沒有找到oencv…

貪心算法理論與實踐總結

文章目錄 一、貪心算法的基本概念二、貪心算法的適用條件三、貪心算法的設計步驟四、貪心算法的經典應用場景1. 區間調度問題2. 背包問題3. 最小生成樹(MST)4. 單源最短路徑(Dijkstra算法)5. 霍夫曼編碼6. 零錢兌換 五、貪心算法的…

在 AWS 上重構數據中臺,這家出海企業選擇了數棧

2024年,袋鼠云接到了一個不小的挑戰。 一家貨幣交易所的技術負責人在通話里直接說:“我們現在業務都跑在 AWS(亞馬遜云平臺) 上了,你們的產品(數棧大數據平臺)能不能不改代碼直接跑在 AWS 上&a…

STM32CubeIDE中文注釋變亂碼終極解決方案:3步設置永久解決錕斤拷問題!

STM32CubeIDE中文注釋變亂碼終極解決方案:3步設置永久解決錕斤拷問題! 前言簡述問題STM32CubeIDE的設置STM32CubeIDE軟件的設置當前工程設置 最重要的一環——添加環境變量重要秘方具體做法 前言 你是否在STM32CubeIDE中遇到過這樣的崩潰場景&#xff1…

Windows VMWare Centos環境下安裝Docker并配置MySql

虛擬機安裝 官網下載Centos Stream 10系統鏡像 安裝了Minimal版,Terminal中粘貼、復制指令不方便,又新建了虛擬機,安裝GUI版 終端輸入指令報錯修復 輸入指令報錯:failed to set locale defaulting to C.UTF-8,安裝語言…

AI能力集成設計與Prompt策略

AI能力集成設計與Prompt策略 在智能客服系統中引入AI能力,必須建立一套架構化、可擴展的AI服務集成體系,并根據不同業務場景制定Prompt策略,從而實現穩定、精準、高效的AI響應能力。 AI能力集成的關鍵組件設計 AI能力集成架構的核心在于通…

深入剖析 CVE-2021-3560 與 CVE-2021-4034:原理、區別與聯系

CVE-2021-3560 和 CVE-2021-4034 是 2021 年曝光的兩個 Linux 本地權限提升漏洞,均涉及 Polkit 組件。由于它們影響廣泛且利用門檻較低,迅速引起安全社區關注。本文將深入分析這兩個漏洞的技術原理、影響范圍、區別與聯系,并結合實際案例&…

Jupyter Notebook 完全指南:從入門到生產力工具

Jupyter Notebook 完全指南:從入門到生產力工具 Jupyter Notebook 已成為數據科學、機器學習和科研領域的標準工具,它完美結合了代碼、文檔和可視化功能。本文將帶您全面了解 Jupyter 的強大功能,并展示如何將其轉化為您的超級生產力工具。 …

HKDF密鑰派生原理與應用詳解

HKDF(HMAC-Based Key Derivation Function)是一種基于 HMAC(Hash-based Message Authentication Code)的密鑰派生函數,用于從原始密鑰材料(如共享密鑰、隨機數等)生成多個加密密鑰(如…

SpringBoot + MyBatis 事務管理全解析:從 @Transactional 到 JDBC Connection 的旅程

SpringBoot MyBatis 事務管理全解析:從 Transactional 到 JDBC Connection 的旅程 一、JDBC Connection:事務操作的真正執行者1.1 數據庫事務的本質1.2 Spring 與 Connection 的協作流程 二、從 Transactional 到 JDBC Connection 的完整鏈路2.1 Spring…

Wpf之應用圖標的修改!

前言 Wpf之應用圖標的修改! 一、修改步驟 1、準備好ico圖片。 2、右鍵項目》點擊屬性 3、找到win32資源點擊 4、點擊瀏覽找到ioc圖標 5、點擊運行程序 6、右鍵項目點擊打開在資源管理器中打開 找到以下路徑 在該路徑下能看到.exe文件的圖標已經改成你想要的…