1.整理的對象和類的c實現(xiàn):
這是大概結(jié)構(gòu)桑驱,具體要比這個復(fù)雜一些泞遗,具體看文件(準(zhǔn)備匆忙隨后貼上源碼):
2.查看Dad類和Child類用clang生成的c代碼:
先解釋一下類結(jié)構(gòu)? 有 Dad(父類) 和 Child(子類) 兩個類, Dad類有 dadName 屬性和 _dadIvarName 成員變量;
Child 類有 childName 屬性和 _childIvarName 成員變量; 兩個類都含有 -(void)testAddress; 和 -(void)printIvars;方法谱轨;(準(zhǔn)備匆忙劈榨,隨后貼上類結(jié)構(gòu)圖)
先是Dad對象用clang生成的c代碼(clang命令在下面):
然后是Child對象:
可以看到即使是繼承關(guān)系中,每個類都有且只有自己的成員變量粒梦,屬性和方法列表亮航;子類中并不包含父類的成員變量,屬性和方法等匀们;
3.查看通過屬性訪問和下劃線成員變量訪問的c代碼:
包括:通過clang生成的c代碼,其中包括:1.通過setter方法訪問父類和子類屬性; 2.通過下劃線_屬性名訪問的父類和子類實例變量;
? ? ? ? ? ? ? ? ? ?- (void)testAddress {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?self.dadName = @"1";
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? _dadIvarName = @"2";
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? self.childName = @"3";
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? _childIvarName = @"4";
? ? ? ? ? ? ? ? ? ? }
命令: ? ? ? clang -rewrite-objc Child.m
? ? ? ?1.可以看到通過setter方法訪問的父類和子類的屬性都是通過消息機(jī)制發(fā)送給self缴淋,但是根據(jù)OC的消息機(jī)制可以知道,如果是父類的方法會通過superclass找到父類,去父類方法列表找到這個方法;這個setter方法是在父類的.m里邊生成的,然后通過給_dadname賦值昼蛀;
? ? ? ? 2.如果是通過下劃線+成員變量名訪問的話宴猾,可以看到如果是父類的成員變量,根據(jù)_dadIvarName生成的代碼中含有的的類名Dad叼旋,以及_childIvarName中含有的類名Child可以知道,其實編譯完成就已經(jīng)知道屬性是屬于哪個類仇哆;
? ? ? ?3.我們可以看到通過成員變量訪問時的c代碼:
? ? ? ?等號的左邊是(self + OBJC_IVAR_$_Dad$_dadIvarName) ? 翻譯一下這不就是:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?成員變量地址 = (對象地址 + 屬于Dad類的_dadIvarName屬性)
? ? ? 或者可以理解為: ? ? ? ? ? ? ?_dadIvarName地址 = 當(dāng)前對象地址 + Dad類中_dadIvarName屬性的偏移量
關(guān)于尋找成員變量的偏移量,貼上王曉磊《Objective-C類成員變量深度剖析》中的一段代碼:
可以看到其實編譯生成的test.ll文件中的LLVM IR代碼里顯示夫植,其實尋找成員變量偏移量是利用分配成全局變量在編譯期間就已經(jīng)確定讹剔,不需要在運(yùn)行時執(zhí)行繁瑣的尋找過程;
4.查看對象中成員變量的內(nèi)存(包括從父類繼承來的以及當(dāng)前類自身的),下圖是打印父類(上)和子類(下)成員變量的代碼:
下圖是輸出的結(jié)果:
根據(jù)上圖可以看到父類和子類成員變量名以及偏移量详民,偏移量是從父類第一變量開始到子類最后一個成員變量順序排布依次增大, 便宜量都依次加8是因為64位下指針是8個字節(jié)(我們的 _dadName 等都是字符串), 第一個成員變量便宜量是8是因為對象第一個內(nèi)容是一個為8個字節(jié)的指向類的isa指針;
下面看看對象的首地址以及對象中各成員變量的地址:
? ? ? ?以下是打印地址的代碼:(子類調(diào)父類同名方法)
以及輸出的結(jié)果:
可以看到延欠,打印的16進(jìn)制地址從父類到子類根據(jù)偏移量依次增長,且符合: ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?成員變量地址 = 對象(首)地址 + 該成員變量的偏移量;
所以可以得出對象在堆中的地址如右圖所示分配:
可以復(fù)習(xí)一下結(jié)構(gòu)體的性質(zhì)沈跨,結(jié)構(gòu)體是按照其中size最大的變量的size來分配內(nèi)存(即為一個變量分配一塊新空間時最小增加量是size)由捎,當(dāng)前一個指針是8個字節(jié),所以便宜量差值都是8饿凛。