【iOS】分類、擴展、關聯對象

分類、擴展、關聯對象

  • 前言
  • 分類
  • 擴展
  • 擴展和分類的區別
  • 關聯對象
    • key的幾種用法
    • 流程
  • 總結

前言

最近的學習中筆者發現自己對于分類、擴展相關知識并不是很熟悉,剛好看源碼類的加載過程中發現有類擴展與關聯對象詳解。本篇我們來探索一下這部分相關知識,首先我們要記住擴展是編譯時就被添加在類中,而分類是在運行時才被整合到類信息中來的

分類

這里我們先來看看使用Clang編譯之后,分類的底層結構struct category_t

在這里插入圖片描述

這里我們來看看其中的內容,根據名稱我們可以發現其中存儲了類指針、實例方法表、類方法表、協議表、屬性列表,但是并沒有類中有的成員變量表。其實看到這里我們就可以明白,我們不可以在分類中定義成員變量,原因很簡單,這里面都沒有成員變量表

這里還有一個結論:分類可以聲明屬性,并可以生成對應的set、get方法,但沒有去實現該方法

分類加載流程:

  • 在編譯階段將分類中的方法、屬性等編譯到一個數據結構category_t
  • 將分類中的方法、屬性等合并到一個大數組中去,而后參加編譯的分類就會在數組前面
  • 將合并后的分類數據插入到原有數據的前面

故而當分類中的方法與原始類中方法重名的時候,會先去調用分類中實現的方法。

擴展

@interface Person ()@property (nonatomic, assign) NSInteger age;  // 私有屬性- (BOOL)validateAge;  // 私有方法聲明@end

這里我們將一個擴展直接使用Clang轉化位cpp文件,我們可以看到其直接被存儲到了成員變量表中,同時方法也直接被添加到了metholist中:

在這里插入圖片描述

在這里插入圖片描述

故而擴展是在編譯階段與該類同時編譯的,是類的一部分。擴展中聲明的方法只能在該類的@implementation中實現。所以這也就意味著我們無法對系統的類使用擴展。

擴展和分類的區別

類別、分類

  • 專門用來給類添加新的方法
  • 不能給類添加成員屬性,添加了成員屬性也無法取到
    • 注意:其實可以通過runtime 給分類添加屬性,即屬性關聯,重寫settergetter方法
  • 分類中用@property 定義變量,只會生成變量的settergetter方法的聲明不能生成方法實現和帶下劃線的成員變量

擴展

  • 可以說成是特殊的分類,也可稱作匿名分類
  • 可以給類添加成員屬性,但是是私有變量
  • 可以給類添加方法,也是私有方法

關聯對象

這里我們來講解一下如何通過runtime來給分類添加屬性,這里主要分為兩部分:

  • 通過objc_setAssociatedObject設值流程
  • 通過objc_getAssociatedObject取值流程

在這里插入圖片描述

流程如上所示

我們先來看看取值流程:

objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,id _Nullable value, objc_AssociationPolicy policy)
  • 參數一:要關聯的對象,即為誰添加關聯屬性
  • 參數二:標識符,方便下次查找
  • 參數三:value
  • 參數四:屬性的策略,即nonatomic、atomic、assign等,下面展示一下所有關聯對象的屬性類型:

在這里插入圖片描述

下面我們來看看objc_setAssociatedObject的源碼實現:

在這里插入圖片描述

下面我們進入_object_set_associative_reference源碼實現來看看:

void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{// This code used to work when nil was passed for object and key. Some code// probably relies on that to not crash. Check and handle it explicitly.// rdar://problem/44094390if (!object && !value) return;if (object->getIsa()->forbidsAssociatedObjects())_objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));//object封裝成一個數組結構類型,類型為DisguisedPtrDisguisedPtr<objc_object> disguised{(objc_object *)object};//相當于包裝了一下 對象object,便于使用// 包裝一下 policy - valueObjcAssociation association{policy, value};// retain the new value (if any) outside the lock.association.acquireValue();//根據策略類型進行處理bool isFirstAssociation = false;{//初始化manager變量,相當于自動調用AssociationsManager的析構函數進行初始化AssociationsManager manager;//并不是全場唯一,構造函數中加鎖只是為了避免重復創建,在這里是可以初始化多個AssociationsManager變量的AssociationsHashMap &associations(manager.get());//AssociationsHashMap 全場唯一if (value) {auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});//返回結構為一個類對if (refs_result.second) {//判斷第二個存不存在,即bool值是否為true/* it's the first association we make */isFirstAssociation = true;}/* establish or replace the association */auto &refs = refs_result.first->second;//得到一個空的桶子,找到引用對象類型,即第一個元素的second值auto result = refs.try_emplace(key, std::move(association));//查找當前的key是否有association關聯對象if (!result.second) {//如果結果不存在association.swap(result.first->second);}} else {//如果傳的是空值,則移除關聯,相當于移除auto refs_it = associations.find(disguised);if (refs_it != associations.end()) {auto &refs = refs_it->second;auto it = refs.find(key);if (it != refs.end()) {association.swap(it->second);refs.erase(it);if (refs.size() == 0) {associations.erase(refs_it);}}}}}// Call setHasAssociatedObjects outside the lock, since this// will call the object's _noteAssociatedObjects method if it// has one, and this may trigger +initialize which might do// arbitrary stuff, including setting more associated objects.if (isFirstAssociation)object->setHasAssociatedObjects();// release the old value (outside of the lock).association.releaseHeldValue();//釋放
}

我們來看看這段源碼的實現過程:

  • 首先檢查對象所屬類是否禁止關聯對象(系統類就不可以),若禁止則直接觸發崩潰
  • 創建一個全局管理關聯對象的AssociationsManager管理類,并獲取唯一的全局靜態哈希Map:AssociationsHashMap
  • value是否存在:
  • 若存在,通過try_emplace方法,創建一個空的ObjectAssociationMap去取查詢鍵值對
  • 如果發現沒有這個 key 就插入一個空的 BucketT進去并返回true
  • 通過setHasAssociatedObjects方法標記對象存在關聯對象即置isa指針的has_assoc屬性為true
  • 用當前policy 和 value組成了一個ObjcAssociation替換原來BucketT 中的空
  • 標記一下 ObjectAssociationMap 的第一次為 false

AssociationsManager

我們先來看看其源碼實現

在這里插入圖片描述

這里我們可以看到AssociationsHashMap從靜態變量中取出,所以全場唯一

下面我們來看看這AssociationsHashMap以及ObjectAssociationMap的定義

在這里插入圖片描述

這里先說一下DenseMap,這個東西時LLVM實現的高性能哈希表,支持快速插入、查找、刪除(筆者具體也不會)

  • 先來看看ObjectAssociationMap,他對應的是一個對象的關聯屬性集合,通過健快速定位到具體的ObjcAssociation

在這里插入圖片描述

? 這里展示一下該結構體內部包含的內容:關聯值的引用計數策略與實際值

  • 再來看看AssociationsHashMap,這是一個全局管理所有對象的關聯屬性的集合,這里鍵為偽裝指針(DisguisedPtr,值為該對象關聯屬性表ObjectAssociationMap

這里附一張圖來講解這幾個表之間的關系

在這里插入圖片描述

下面來說一下這幾個map之間的聯系與不同:

  • AssociationsManager可以有很多個,但是AssociationsHashMap類型的map只能有一個,是通過AssociationsManager來獲取的
  • 這個map中有很多個ObjectAssociationMap類型的map,在上文中的講解中,我們可以明白每個對象都有一個ObjcAssociation,所以每個對象都會有一個自己的ObjectAssociationMap類型的map

key的幾種用法

  • 使用的get方法的@selector作為key
objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, @selector(getter))
  • 使用指針的地址作為key
static void *MyKey = &MyKey;
objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, MyKey)
  • 使用static作為key
static char MyKey;objc_setAssociatedObject(obj, &MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, &MyKey)
  • 使用屬性名作為key
objc_setAssociatedObject(obj, @“property”, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_getAssociatedObject(obj, @“property”);

流程

  • 設置關聯對象:

    • 調用objc_setAssociatedObject

    • AssociationsManager查找或創建與目標對象相關的ObjectAssociationMap

    • ObjectAssociationMap中查找或創建對應的 ObjcAssociation

    • 將關聯值和存儲策略設置到 ObjcAssociation 中。

  • 獲取關聯對象:

    • 調用objc_setAssociatedObject

    • AssociationsManager查找與目標對象相關的ObjectAssociationMap

    • ObjectAssociationMap中查找對應的 ObjcAssociation

    • 返回ObjcAssociation中存儲的關聯值

  • 移除關聯對象:

    • 調用 objc_removeAssociatedObjectsobjc_setAssociatedObject 設置為 nil。
    • AssociationsManager 查找與目標對象相關的ObjectAssociationMap
    • ObjectAssociationMap 中移除對應的 ObjcAssociation
    • 如果ObjectAssociationMap為空,可能會移除整個映射以釋放資源。

這個流程其實也就是上文中_object_set_associative_reference的流程,筆者認為這樣理解更好一些,下面再附一張圖幫助理解

在這里插入圖片描述

總結

關聯對象就是一個二層哈希的處理,存取的時候都是兩層處理,類似于二維數組:
在這里插入圖片描述

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

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

相關文章

30.第二階段x64游戲實戰-認識網絡數據包發送流程

免責聲明&#xff1a;內容僅供學習參考&#xff0c;請合法利用知識&#xff0c;禁止進行違法犯罪活動&#xff01; 內容參考于&#xff1a;圖靈Python學院 上一個內容&#xff1a;29.第二階段x64游戲實戰-技能冷卻 發送數據包的方式&#xff08;函數&#xff09;操作系統提供…

【每日一題】【前綴和優化】【前/后綴最值】牛客練習賽139 B/C題 大衛的密碼 (Hard Version) C++

牛客練習賽139 B題 大衛的密碼 (Easy Version) 牛客練習賽139 C題 大衛的密碼 (Hard Version) 大衛的密碼 題目背景 牛客練習賽139 題目描述 給定一個 n m n\times m nm的網格圖&#xff0c;我們使用 ( i , j ) (i,j) (i,j)表示網格中從上往下數第 i i i行和從左往右數第…

文件夾圖像批處理教程

前言 因為經常對圖像要做數據清洗&#xff0c;又很費時間去重新寫一個&#xff0c;我一直在想能不能寫一個通用的腳本或者制作一個可視化的界面對文件夾圖像做批量的修改圖像大小、重命名、劃分數據訓練和驗證集等等。這里我先介紹一下我因為寫過的一些腳本&#xff0c;然后我…

【Unity實戰筆記】第二十四 · 使用 SMB+Animator 實現基礎戰斗系統

轉載請注明出處&#xff1a;&#x1f517;https://blog.csdn.net/weixin_44013533/article/details/146409453 作者&#xff1a;CSDN|Ringleader| 1 結構 1.1 狀態機 1.2 SMB 2 代碼實現 2.1 核心控制 Player_Base_SMB 繼承 StateMachineBehaviour &#xff0c;控制變量初始…

Python虛擬環境再PyCharm中自由切換使用方法

Python開發中的環境隔離是必不可少的步驟,通過使用虛擬環境可以有效地管理不同項目間的依賴,避免包沖突和環境污染。虛擬環境是Python官方提供的一種獨立運行環境,每個項目可以擁有自己單獨的環境,不同項目之間的環境互不影響。在日常開發中,結合PyCharm這樣強大的IDE進行…

大模型智能體入門掃盲——基于camel的概述

前言 本篇博客想帶讀者進行一個智能體入門掃盲&#xff0c;了解基礎知識&#xff0c;為什么用camel呢&#xff0c;因為小洛發現它們文檔對這種智能體的基本組件介紹得很全面深入。 基礎概念 agent 一個典型的agent智能體包含三個核心部分&#xff1a; 感知模塊&#xff1…

目標檢測 RT-DETR(2023)詳細解讀

文章目錄 主干網絡&#xff1a;Encoder&#xff1a;不確定性最小Query選擇Decoder網絡&#xff1a; 將DETR擴展到實時場景&#xff0c;提高了模型的檢測速度。網絡架構分為三部分組成&#xff1a;主干網絡、混合編碼器、帶有輔助預測頭的變換器編碼器。具體來說&#xff0c;先利…

DeepSeek 賦能數字農業:從智慧種植到產業升級的全鏈條革新

目錄 一、數字農業的現狀與挑戰二、DeepSeek 技術解析2.1 DeepSeek 的技術原理與優勢2.2 DeepSeek 在人工智能領域的地位與影響力 三、DeepSeek 在數字農業中的應用場景3.1 精準種植決策3.2 病蟲害監測與防治3.3 智能灌溉與施肥管理3.4 農產品質量追溯與品牌建設 四、DeepSeek …

<uniapp><vuex><狀態管理>在uniapp中,如何使用vuex實現數據共享與傳遞?

前言 本專欄是基于uniapp實現手機端各種小功能的程序&#xff0c;并且基于各種通訊協議如http、websocekt等&#xff0c;實現手機端作為客戶端&#xff08;或者是手持機、PDA等&#xff09;&#xff0c;與服務端進行數據通訊的實例開發。 發文平臺 CSDN 環境配置 系統&…

高速串行差分信號仿真分析及技術發展挑戰續

7.3 3.125Gbps 差分串行信號設計實例仿真分析 7.3.1 設計用例說明 介紹完 Cadence 系統本身所具有的高速差分信號的仿真分析功能之后&#xff0c;我們以一個實例來說明 3.125Gbps 以下的高速差分系統的仿真分析方法。 在網上下載的設計文件“Booksi_Demo_Allegro160_Finishe…

【Golang】部分語法格式和規則

1、時間字符串和時間戳的相互轉換 func main() {t1 : int64(1546926630) // 外部傳入的時間戳&#xff08;秒為單位&#xff09;&#xff0c;必須為int64類型t2 : "2019-01-08 13:50:30" // 外部傳入的時間字符串//時間轉換的模板&#xff0c;golang里面只能是 &quo…

第十六章:數據治理之數據架構:數據模型和數據流轉關系

本章我們說一下數據架構&#xff0c;說到數據架構&#xff0c;就很自然的想到企業架構、業務架構、軟件架構&#xff0c;因為個人并沒有對這些內容進行深入了解&#xff0c;所以這里不做比對是否有相似或者共通的地方&#xff0c;僅僅來說一下我理解的數據架構。 1、什么是架構…

Day126 | 靈神 | 二叉樹 | 層數最深的葉子結點的和

Day126 | 靈神 | 二叉樹 | 層數最深的葉子結點的和 1302.層數最深的葉子結點的和 1302. 層數最深葉子節點的和 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 這道題用層序遍歷的思路比較好想&#xff0c;就把每層的都算一下&#xff0c;然后返回最后一層的和就…

PCIE 4.0 vs PCIE 5.0固態硬盤——區別、科普與選購場景全解析

隨著數字內容和高性能計算需求的爆發&#xff0c;固態硬盤&#xff08;SSD&#xff09;已成為PC、游戲主機和工作站不可或缺的核心硬件。面對市面上層出不窮的新一代SSD產品&#xff0c;大家最常見的一個疑惑&#xff1a;**PCIe 4.0和PCIe 5.0固態硬盤&#xff0c;到底有啥區別…

vue pinia 獨立維護,倉庫統一導出

它允許您跨組件/頁面共享狀態 持久化 安裝依賴pnpm i pinia-plugin-persistedstate 將插件添加到 pinia 實例上 pinia獨立維護 統一導出 import { createPinia } from pinia import piniaPluginPersistedstate from pinia-plugin-persistedstateconst pinia creat…

Dify源碼學習

文章目錄 1 大模型基本原理1.1 model_context_tokens、max_tokens和prompt_tokens1.1.1 三者之間的關系1.1.2 總結對比 2 Dify源代碼2.0 前后端代碼跑起來【0】準備開發環境【1】下載代碼【2】運行后端&#xff08;1&#xff09;Start the docker-compose stack&#xff08;2&a…

連接表、視圖和存儲過程

1. 視圖 1.1. 視圖的概念 視圖&#xff08;View&#xff09;&#xff1a;虛擬表&#xff0c;本身不存儲數據&#xff0c;而是封裝了一個 SQL 查詢的結果集。 用途&#xff1a; 只顯示部分數據&#xff0c;提高數據訪問的安全性。簡化復雜查詢&#xff0c;提高復用性和可維護…

微信小程序中,解決lottie動畫在真機不顯示的問題

api部分 export function getRainInfo() {return onlineRequest({url: /ball/recruit/getRainInfo,method: get}); }data存儲json數據 data&#xff1a;{rainJson:{} }onLoad方法獲取json數據 onLoad(options) {let that thisgetRainInfo().then((res)>{that.setData({r…

從加密到信任|密碼重塑車路云一體化安全生態

目錄 一、密碼技術的核心支撐 二、典型應用案例 三、未來發展方向 總結 車路云系統涉及海量實時數據交互&#xff0c;包括車輛位置、傳感器信息、用戶身份等敏感數據。其安全風險呈現三大特征&#xff1a; 開放環境威脅&#xff1a;V2X&#xff08;車與萬物互聯&#xff0…

光譜相機在地質勘測中的應用

一、?礦物識別與蝕變帶分析? ?光譜特征捕捉? 通過可見光至近紅外&#xff08;400-1000nm&#xff09;的高光譜分辨率&#xff08;可達3.5nm&#xff09;&#xff0c;精確識別礦物的“光譜指紋”。例如&#xff1a; ?銅礦?&#xff1a;在400-500nm波段反射率顯著低于圍…