isa指針相關(guān)

整理一下自己學(xué)到的知識(shí),方便以后查看

1.向nil對象發(fā)送消息易迹,為什么不會(huì)崩潰

每個(gè)想法在運(yùn)行時(shí)夭委,都會(huì)被動(dòng)態(tài)轉(zhuǎn)為消息發(fā)送,即ojbc_msgSend(receiver,selector)能岩。

struct objc_class {
  Class isa OBJC_ISA_AVAILABILITY; //isa指針指向Meta Class寞宫,因?yàn)镺bjc的類的本身也是一個(gè)Object,為了處理這個(gè)關(guān)系拉鹃,runtime就創(chuàng)造了Meta Class辈赋,當(dāng)給類發(fā)送[NSObject alloc]這樣消息時(shí),實(shí)際上是把這個(gè)消息發(fā)給了Class Object
  #if !__OBJC2__
  Class super_class OBJC2_UNAVAILABLE; // 父類
  const char *name OBJC2_UNAVAILABLE; // 類名
  long version OBJC2_UNAVAILABLE; // 類的版本信息膏燕,默認(rèn)為0
  long info OBJC2_UNAVAILABLE; // 類信息钥屈,供運(yùn)行期使用的一些位標(biāo)識(shí)
  long instance_size OBJC2_UNAVAILABLE; // 該類的實(shí)例變量大小
  struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 該類的成員變量鏈表
  struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定義的鏈表
  struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法緩存,對象接到一個(gè)消息會(huì)根據(jù)isa指針查找消息對象坝辫,這時(shí)會(huì)在method Lists中遍歷篷就,如果cache了,常用的方法調(diào)用時(shí)就能夠提高調(diào)用的效率近忙。
  struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 協(xié)議鏈表
  #endif
  } OBJC2_UNAVAILABLE;

向?qū)ο蟀l(fā)送消息時(shí)竭业,runtime會(huì)根據(jù)對象的isa指針找到它的類對象,從類對象中方法列表以及其父類方法列表中尋找方法運(yùn)行银锻,發(fā)送信息是objc_msgSend方法不會(huì)返回值永品,所謂的返回內(nèi)容都是具體調(diào)用時(shí)執(zhí)行的。 如果對一個(gè)nil對象發(fā)送信息击纬,對象的isa指針就是返回0地址了鼎姐,所以不會(huì)出現(xiàn)崩潰。

轉(zhuǎn)載一張非常有用且非常利于理解的圖:


image.png

KVO實(shí)現(xiàn)原理(isa混寫)

當(dāng)你觀察一個(gè)對象時(shí)更振,一個(gè)新的類會(huì)被動(dòng)態(tài)創(chuàng)建炕桨。這個(gè)類繼承自該對象的原本的類,并重寫了被觀察屬性的 setter 方法肯腕。重寫的 setter 方法會(huì)負(fù)責(zé)在調(diào)用原 setter 方法之前和之后献宫,通知所有觀察對象:值的更改。最后通過 isa 混寫(isa-swizzling) 把這個(gè)對象的 isa 指針 ( isa 指針告訴 Runtime 系統(tǒng)這個(gè)對象的類是什么 ) 指向這個(gè)新創(chuàng)建的子類实撒,對象就神奇的變成了新創(chuàng)建的子類的實(shí)例姊途。我畫了一張示意圖,如下所示:


KVO 確實(shí)有點(diǎn)黑魔法:

Apple 使用了 isa 混寫(isa-swizzling)來實(shí)現(xiàn) KVO 知态。

下面做下詳細(xì)解釋:

鍵值觀察通知依賴于 NSObject 的兩個(gè)方法: willChangeValueForKey: 和 didChangevlueForKey: 捷兰。在一個(gè)被觀察屬性發(fā)生改變之前, willChangeValueForKey: 一定會(huì)被調(diào)用负敏,這就 會(huì)記錄舊的值贡茅。而當(dāng)改變發(fā)生后, didChangeValueForKey: 會(huì)被調(diào)用,繼而 observeValueForKey:ofObject:change:context: 也會(huì)被調(diào)用顶考×藁梗可以手動(dòng)實(shí)現(xiàn)這些調(diào)用,但很少有人這么做驹沿。一般我們只在希望能控制回調(diào)的調(diào)用時(shí)機(jī)時(shí)才會(huì)這么做艘策。大部分情況下,改變通知會(huì)自動(dòng)調(diào)用甚负。

比如調(diào)用 setNow: 時(shí)柬焕,系統(tǒng)還會(huì)以某種方式在中間插入 wilChangeValueForKey: 、 didChangeValueForKey: 和 observeValueForKeyPath:ofObject:change:context: 的調(diào)用梭域。大家可能以為這是因?yàn)?setNow: 是合成方法斑举,有時(shí)候我們也能看到人們這么寫代碼:

- (void)setNow:(NSDate *)aDate {
    [self willChangeValueForKey:@"now"]; // 沒有必要
    _now = aDate;
    [self didChangeValueForKey:@"now"];// 沒有必要
}

這是完全沒有必要的代碼,不要這么做病涨,這樣的話富玷,KVO代碼會(huì)被調(diào)用兩次。KVO在調(diào)用存取方法之前總是調(diào)用 willChangeValueForKey: 既穆,之后總是調(diào)用 didChangeValueForkey: 赎懦。怎么做到的呢?答案是通過 isa 混寫(isa-swizzling)。第一次對一個(gè)對象調(diào)用 addObserver:forKeyPath:options:context: 時(shí)幻工,框架會(huì)創(chuàng)建這個(gè)類的新的 KVO 子類励两,并將被觀察對象轉(zhuǎn)換為新子類的對象。在這個(gè) KVO 特殊子類中囊颅, Cocoa 創(chuàng)建觀察屬性的 setter 当悔,大致工作原理如下:

- (void)setNow:(NSDate *)aDate {
    [self willChangeValueForKey:@"now"];
    [super setValue:aDate forKey:@"now"];
    [self didChangeValueForKey:@"now"];
}

這種繼承和方法注入是在運(yùn)行時(shí)而不是編譯時(shí)實(shí)現(xiàn)的。這就是正確命名如此重要的原因踢代。只有在使用KVC命名約定時(shí)盲憎,KVO才能做到這一點(diǎn)。

KVO 在實(shí)現(xiàn)中通過 isa 混寫(isa-swizzling) 把這個(gè)對象的 isa 指針 ( isa 指針告訴 Runtime 系統(tǒng)這個(gè)對象的類是什么 ) 指向這個(gè)新創(chuàng)建的子類胳挎,對象就神奇的變成了新創(chuàng)建的子類的實(shí)例饼疙。這在Apple 的文檔可以得到印證:

Automatic key-value observing is implemented using a technique called isa-swizzling... When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class ...

然而 KVO 在實(shí)現(xiàn)中使用了 isa 混寫( isa-swizzling) ,這個(gè)的確不是很容易發(fā)現(xiàn):Apple 還重寫慕爬、覆蓋了 -class 方法并返回原來的類窑眯。 企圖欺騙我們:這個(gè)類沒有變,就是原本那個(gè)類医窿。伸但。。

但是留搔,假設(shè)“被監(jiān)聽的對象”的類對象是 MYClass ,有時(shí)候我們能看到對 NSKVONotifying_MYClass 的引用而不是對 MYClass 的引用铛铁。借此我們得以知道 Apple 使用了 isa 混寫(isa-swizzling)隔显。具體探究過程可參考 這篇博文 却妨。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市括眠,隨后出現(xiàn)的幾起案子彪标,更是在濱河造成了極大的恐慌,老刑警劉巖掷豺,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捞烟,死亡現(xiàn)場離奇詭異,居然都是意外死亡当船,警方通過查閱死者的電腦和手機(jī)题画,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來德频,“玉大人苍息,你說我怎么就攤上這事∫贾茫” “怎么了竞思?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長钞护。 經(jīng)常有香客問我盖喷,道長,這世上最難降的妖魔是什么难咕? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任课梳,我火速辦了婚禮,結(jié)果婚禮上步藕,老公的妹妹穿的比我還像新娘惦界。我一直安慰自己,他們只是感情好咙冗,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布沾歪。 她就那樣靜靜地躺著,像睡著了一般雾消。 火紅的嫁衣襯著肌膚如雪灾搏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天立润,我揣著相機(jī)與錄音狂窑,去河邊找鬼。 笑死桑腮,一個(gè)胖子當(dāng)著我的面吹牛泉哈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼丛晦,長吁一口氣:“原來是場噩夢啊……” “哼奕纫!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起烫沙,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤匹层,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后锌蓄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體升筏,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年瘸爽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了您访。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,673評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蝶糯,死狀恐怖洋只,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情昼捍,我是刑警寧澤识虚,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站妒茬,受9級特大地震影響担锤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜乍钻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一肛循、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧银择,春花似錦多糠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至析孽,卻和暖如春搭伤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背袜瞬。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工怜俐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人邓尤。 一個(gè)月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓拍鲤,卻偏偏與公主長得像贴谎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子殿漠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評論 2 349

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