【iOS 底層原理】block

1.使用 runtime 為 Category 動態(tài)關(guān)聯(lián)對象

在分類中可以用 @property 添加屬性钠右,但是不會自動生成私有成員變量嗤疯,也不會生成 set/get 方法的實現(xiàn)壹堰,只會生成 set/get 的聲明,所以要在分類中添加屬性,需要借助關(guān)聯(lián)對象运提。

使用關(guān)聯(lián)對象模擬屬性

相關(guān) API

objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
objc_getAssociatedObject(id object, const void *key);

說明:

  • 參數(shù)一:id object : 給哪個對象添加屬性鳍寂,這里要給自己添加屬性改含,用 self。
  • 參數(shù)二:void * == id key : 屬性名迄汛,根據(jù)key獲取關(guān)聯(lián)對象的屬性的值捍壤,在objc_getAssociatedObject中通過次key獲得屬性的值并返回骤视。key值只要是一個指針即可,我們可以傳入@selector(name)
  • 參數(shù)三:id value : 關(guān)聯(lián)的值鹃觉,也就是set方法傳入的值給屬性去保存专酗。
  • 參數(shù)四:objc_AssociationPolicy policy : 策略,屬性以什么形式保存盗扇。有以下幾種
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,  // 指定一個弱引用相關(guān)聯(lián)的對象
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, // 指定相關(guān)對象的強引用祷肯,非原子性
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  // 指定相關(guān)的對象被復(fù)制,非原子性
    OBJC_ASSOCIATION_RETAIN = 01401,  // 指定相關(guān)對象的強引用疗隶,原子性
    OBJC_ASSOCIATION_COPY = 01403     // 指定相關(guān)的對象被復(fù)制佑笋,原子性   
};
image.png

key 的常見定義方法

static void *MyKey = &MyKey;
objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, MyKey)

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");

使用get方法的@selecor作為key
objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, @selector(getter))

demo

-(void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, @"name",name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSString *)name
{
    return objc_getAssociatedObject(self, @"name");    
}

- (void)removeAssociatedObjects
{
    // 移除所有關(guān)聯(lián)對象
    objc_removeAssociatedObjects(self);
    // 移除單個關(guān)聯(lián)對象,置我 nil 即可
    self.name = nil;
}

2.關(guān)聯(lián)對象實現(xiàn)原理

關(guān)聯(lián)對象相關(guān)類

  • AssociationsManager
  • AssociationsHashMap
  • ObjectAssociationMap
  • ObjcAssociation

相關(guān)結(jié)構(gòu)

image.png

原理圖示

image.png

3.源碼解讀

objc_setAssociatedObject 函數(shù)

objc_setAssociatedObject 函數(shù)


image.png

_object_set_associative_reference 函數(shù)


image.png

AssociationsManager

通過 AssociationsManager 內(nèi)部源碼發(fā)現(xiàn)抽减,AssociationsManager 內(nèi)部有一個AssociationsHashMap 對象允青。


image.png

AssociationsHashMap

image.png

通過 AssociationsHashMap 內(nèi)部源碼我們發(fā)現(xiàn) AssociationsHashMap 繼承自 unordered_map 首先來看一下 unordered_map 內(nèi)的源碼


image.png

從 unordered_map 源碼中我們可以看出 _Key 和 _Tp 也就是前兩個參數(shù)對應(yīng)著map中的Key和Value,那么對照上面 AssociationsHashMap 內(nèi)源碼發(fā)現(xiàn) _Key 中傳入的是disguised_ptr_t卵沉,_Tp 中傳入的值則為 ObjectAssociationMap*颠锉。

ObjectAssociationMap 中同樣以 key、Value 的方式存儲著 ObjcAssociation史汗。

ObjcAssociation 中


image.png

我們發(fā)現(xiàn)ObjcAssociation存儲著_policy琼掠、_value,而這兩個值我們可以發(fā)現(xiàn)正是我們調(diào)用objc_setAssociatedObject函數(shù)傳入的值停撞,也就是說我們在調(diào)用objc_setAssociatedObject函數(shù)中傳入的value和policy這兩個值最終是存儲在ObjcAssociation中的瓷蛙。

總結(jié)圖

image.png

關(guān)聯(lián)對象并不是存儲在被關(guān)聯(lián)對象本身內(nèi)存中,而是存儲在全局的統(tǒng)一的一個AssociationsManager 中戈毒,如果設(shè)置關(guān)聯(lián)對象為 nil艰猬,就相當(dāng)于是移除關(guān)聯(lián)對象。

面試題

  1. Category 能否添加成員變量埋市?如果可以冠桃,如何給 Category 添加成員變量?

答:默認(rèn)情況下道宅,因為分類底層結(jié)構(gòu)的限制食听,不能添加成員變量到分類中,但是可以通過 runtime 的方式間接實現(xiàn)添加成員變量的效果污茵。
分類底層結(jié)構(gòu)沒有 ivar_list 這個結(jié)構(gòu)樱报。

  1. 關(guān)聯(lián)對象的 policy 中為何沒有 weak

答:關(guān)聯(lián)對象是保存在一個全局的 Map 中,此 Map 為 HashMap泞当,內(nèi)部不能保存一個弱引用對象

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末迹蛤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌笤受,老刑警劉巖穷缤,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異箩兽,居然都是意外死亡,警方通過查閱死者的電腦和手機章喉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門汗贫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人秸脱,你說我怎么就攤上這事落包。” “怎么了摊唇?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵咐蝇,是天一觀的道長。 經(jīng)常有香客問我巷查,道長有序,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任岛请,我火速辦了婚禮旭寿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘崇败。我一直安慰自己盅称,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布后室。 她就那樣靜靜地躺著缩膝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪岸霹。 梳的紋絲不亂的頭發(fā)上疾层,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機與錄音松申,去河邊找鬼云芦。 笑死,一個胖子當(dāng)著我的面吹牛贸桶,可吹牛的內(nèi)容都是我干的舅逸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼皇筛,長吁一口氣:“原來是場噩夢啊……” “哼琉历!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤旗笔,失蹤者是張志新(化名)和其女友劉穎彪置,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝇恶,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡拳魁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了撮弧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片潘懊。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖贿衍,靈堂內(nèi)的尸體忽然破棺而出授舟,到底是詐尸還是另有隱情,我是刑警寧澤贸辈,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布释树,位于F島的核電站,受9級特大地震影響擎淤,放射性物質(zhì)發(fā)生泄漏奢啥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一揉燃、第九天 我趴在偏房一處隱蔽的房頂上張望扫尺。 院中可真熱鬧,春花似錦炊汤、人聲如沸正驻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姑曙。三九已至,卻和暖如春迈倍,著一層夾襖步出監(jiān)牢的瞬間伤靠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工啼染, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宴合,地道東北人。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓迹鹅,卻偏偏與公主長得像卦洽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子斜棚,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容