深入理解 KVO

在 iOS 中,KVO(Key-Value Observing)是一個強大的觀察機制,它的底層實現相對復雜。KVO 利用 Objective-C 的動態特性,為對象的屬性提供觀察能力。

KVO 的底層實現

1. 動態子類化

當一個對象的屬性被添加觀察者時,KVO 會在運行時動態地創建該對象的子類,并重寫該屬性的 setter 方法。

  1. 動態創建子類:KVO 會創建一個新的類,這個新類是被觀察對象的子類,通常這個類的名字是 _NSKVOClassName_ClassName 形式。
  2. 重寫 setter 方法:在這個動態創建的子類中,KVO 會重寫被觀察屬性的 setter 方法。

2. 重寫 setter 方法

重寫后的 setter 方法在屬性值發生變化時,會進行以下操作:

  1. 觸發 willChangeValue(forKey:):通知即將發生變化。
  2. 調用原始 setter 方法:通過消息轉發機制調用原始的 setter 方法,以實際更新屬性值。
  3. 觸發 didChangeValue(forKey:):通知變化已經發生,觸發觀察者回調。

3. 動態方法解析

在 KVO 動態創建的子類中,使用 method_setImplementation 方法來重寫屬性的 setter 方法。

void setAge(id self, SEL _cmd, int newAge) {[self willChangeValueForKey:@"age"];struct objc_super superStruct = {.receiver = self,.super_class = class_getSuperclass(object_getClass(self))};((void (*)(struct objc_super *, SEL, int))objc_msgSendSuper)(&superStruct, _cmd, newAge);[self didChangeValueForKey:@"age"];
}

KVO 的實現細節

以下是一個簡單的示例,展示了 KVO 的一些底層實現細節:

@interface Person : NSObject
@property (nonatomic, assign) int age;
@end@implementation Person
@endPerson *person = [[Person alloc] init];
NSLog(@"Original class: %@", object_getClass(person)); // 輸出原始類[person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
NSLog(@"Class after adding observer: %@", object_getClass(person)); // 輸出動態子類[person setAge:30];
[person removeObserver:self forKeyPath:@"age"];

KVO 的工作流程

  1. 添加觀察者

    • 調用 addObserver:forKeyPath:options:context: 方法時,KVO 會動態創建子類并重寫 setter 方法。
    • 原始對象的類指針(isa 指針)被修改為新創建的子類。
  2. 觸發觀察

    • 當屬性值發生變化時,調用重寫后的 setter 方法。
    • 先觸發 willChangeValueForKey:,然后調用原始 setter 方法更新屬性值,最后觸發 didChangeValueForKey:
    • 觸發 didChangeValueForKey: 時,會通知所有觀察者屬性值已經改變。
  3. 移除觀察者

    • 調用 removeObserver:forKeyPath: 方法時,KVO 會將類指針恢復為原始類,并移除重寫的 setter 方法。

注意事項

  • 自動 KVO:KVO 默認僅支持通過 setter 方法修改屬性值的情況。直接修改實例變量不會觸發 KVO。
  • 手動觸發 KVO:如果需要手動觸發 KVO,可以調用 willChangeValue(forKey:)didChangeValue(forKey:) 方法。
[self willChangeValueForKey:@"age"];
_age = newValue;
[self didChangeValueForKey:@"age"];

總結

KVO 是 iOS 中基于動態特性實現的觀察機制,通過動態子類化和方法重寫實現。當屬性值變化時,KVO 會通知所有注冊的觀察者。這一機制使得對象間的通信更加靈活和高效,但也需要注意在使用過程中正確添加和移除觀察者,以避免內存泄漏或崩潰。

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

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

相關文章

6、Redis系統-數據結構-01-String

Redis 數據結構簡介 前言 Redis 是一個高性能的內存數據庫,其關鍵在于其數據結構的設計。Redis 數據結構是指底層實現 Redis 鍵值對中值的數據類型的方式。它包括了以下幾種主要對象: String(字符串)對象:最基本的數…

[C++][CMake][流程控制]詳細講解

目錄 1.條件判斷1.基本表達式2.邏輯判斷3.比較4.文件操作5.其他 2.循環1.foreach2.while 1.條件判斷 在進行條件判斷的時候,如果有多個條件,那么可以寫多個elseif,最后一個條件可以使用else,但是開始和結束是必須要成對出現的&am…

WordPress常見問題及簡要說明

1. 如何安裝WordPress? 簡要說明:WordPress是一個流行的內容管理系統,可以幫助用戶快速搭建網站。安裝WordPress需要以下幾個步驟:下載WordPress安裝包、上傳到服務器、創建數據庫、配置數據庫信息、完成安裝。 2. 如何創建一個新的WordPr…

掌握電量脈搏:WebKit 電池狀態(Battery Status API)支持全解析

掌握電量脈搏:WebKit 電池狀態(Battery Status API)支持全解析 隨著移動設備的廣泛使用,Web 應用對設備的電池狀態信息的需求日益增長。Battery Status API 提供了一種方式,允許 Web 應用訪問設備的電池信息&#xff…

【反悔貪心 反悔堆】1642. 可以到達的最遠建筑

本文涉及知識點 反悔貪心 反悔堆 LeetCode1642. 可以到達的最遠建筑 給你一個整數數組 heights ,表示建筑物的高度。另有一些磚塊 bricks 和梯子 ladders 。 你從建筑物 0 開始旅程,不斷向后面的建筑物移動,期間可能會用到磚塊或梯子。 當…

Spring Boot中的全局異常處理

Spring Boot中的全局異常處理 大家好,我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編,也是冬天不穿秋褲,天冷也要風度的程序猿!今天我們將探討如何在Spring Boot應用中實現全局異常處理,這是保證應用…

VSCode, 請在windows下使用git bash終端

用vscode在windows下調測代碼,運行時默認打開的終端是windows的cmd,很不受我待見。畢竟習慣了linux,習慣了windows下的git bash風格。怎么辦? search,search,research。 先確保windows上安裝了git bash。…

MATLAB 2024b 更新了些什么?

MATLAB 2024b版本已經推出了預覽版,本期介紹一些MATLAB部分的主要的更新內容。 幫助瀏覽器被移除 在此前的版本,當我們從MATLAB中訪問幫助文檔時,默認會通過MATLAB的幫助瀏覽器(Help browser)。 2024b版本開始&…

【Unity數據交互】如何Unity中讀取Ecxel中的數據

👨?💻個人主頁:元宇宙-秩沅 👨?💻 hallo 歡迎 點贊👍 收藏? 留言📝 加關注?! 👨?💻 本文由 秩沅 原創 👨?💻 專欄交流🧧&…

醫院掛號系統小程序的設計

管理員賬戶功能包括:系統首頁,個人中心,患者管理,醫生管理,專家信息管理,科室管理,預約信息管理,系統管理 微信端賬號功能包括:系統首頁,專家信息&#xff0…

數據結構算法-排序(一)-冒泡排序

什么是冒泡排序 冒泡排序:在原數組中通過相鄰兩項元素的比較,交換而完成的排序算法。 算法核心 數組中相鄰兩項比較、交換。 算法復雜度 時間復雜度 實現一次排序找到最大值需要遍歷 n-1次(n為數組長度) 需要這樣的排序 n-1次。 需要 (n-1) * (n-1) —…

Java事務(Transaction)

Java事務(Transaction)是數據庫管理系統執行過程中的一個邏輯單位,由一個有限的數據庫操作序列組成,這些操作要么全部執行,要么全部不執行,是一個不可分割的工作單位。事務的引入主要是為了解決并發操作數據…

Unity中遇到“Input Button unload_long_back_btn is not setup”問題

當你在Unity中遇到“Input Button unload_long_back_btn is not setup”這個問題時,需要按照以下步驟進行處理: 1. 檢查按鈕名稱 確保你在代碼中使用的按鈕名稱(unload_long_back_btn)與Unity輸入管理器中的配置完全匹配。 2. …

[AIGC] ClickHouse分布式表與本地表的區別及如何查詢所有本地表記錄

在大規模數據處理和分析場景中,ClickHouse是一種高性能的列式數據庫管理系統。ClickHouse支持分布式表和本地表兩種表類型,本文將介紹這兩種表類型的區別,并探討如何建表以查詢所有本地表的記錄。 文章目錄 一、ClickHouse分布式表與本地表的…

【Linux進階】文件系統7——文件系統簡單操作

1.磁盤與目錄的容量 現在我們知道磁盤的整體數據是在超級區塊中,但是每個文件的容量則在inode 當中記載。 那在命令行模式下面該如何顯示這幾個數據?下面就讓我們來談一談這兩個命令: df:列出文件系統的整體磁盤使用量&#xf…

Poker Game, Run Fast

Poker Game, Run Fast 撲克&#xff1a;跑得快 分門別類&#xff1a; 單張從小到大默認 A < 2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 < J < Q < K 跑得快&#xff1a;單張從小到大 3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 &…

javaweb個人主頁設計(html+css+js)

目錄 1 前言和要求 1.1 前言 1.2 設計要求 2 預覽 2.1 主頁頁面 2.2 個人簡介 2.3 個人愛好 2.4 個人成績有代碼&#xff0c;但是圖片已省略&#xff0c;可以根據自己情況添加 2.5 收藏夾 3 代碼實現 3.1 主頁 3.2 個人簡介 3.3 個人愛好 3.4 個人成績&#xff…

大數據處理利器:Apache Spark編程基礎與實戰

"大數據處理利器&#xff1a;Apache Spark編程基礎與實戰" 是一個涵蓋了Apache Spark這一強大大數據處理框架的深入學習和實踐指南。Apache Spark是一個快速、通用、可擴展的大數據處理引擎&#xff0c;它提供了高級別的API用于大規模數據處理和分析。下面&#xff0…

求職成功率的算法,與葫蘆娃救爺爺的算法,有哪些相同與不同

1 本節概述 通過在B站百刷葫蘆娃這部兒時劇&#xff0c;我覺得可以從中梳理出一些算法&#xff0c;甚至可以用于求職這個場景。所以&#xff0c;大家可以隨便問我葫蘆娃的一些劇情和感悟&#xff0c;我都可以做一些回答。 2 葫蘆娃救爺爺有哪些算法可言&#xff1f; 我們知道…

身體(body)的覺醒

佛&#xff0c;是一個梵文的漢語音譯詞&#xff0c;指覺醒者。 何謂覺醒&#xff1f;什么的覺醒&#xff1f;其實很簡單&#xff0c;就是身體的覺醒。 佛的另一個名字&#xff0c;叫菩提&#xff0c;佛就是菩提&#xff0c;菩提老祖&#xff0c;就是佛祖。 body&#xff0c;即…