淺談對(duì)Objective-C的對(duì)象本質(zhì)的理解

1. Objective-C的本質(zhì)

我們平時(shí)編寫的OC代碼,其實(shí)底層實(shí)現(xiàn)都是C/C++代碼,類主要是基于C/C++的結(jié)構(gòu)體的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的,因?yàn)閷?duì)象或者類有各種類型(NSArray *沛硅,NSDictionary *,CFfloat等),因?yàn)榭梢源鎯?chǔ)不同種類的數(shù)據(jù),能夠滿足的這樣的結(jié)構(gòu)就是結(jié)構(gòu)體.
為了證明OC的結(jié)構(gòu),所以可以轉(zhuǎn)換成C++的代碼,窺探內(nèi)部的結(jié)構(gòu)(有時(shí)候C++的代碼也不一定能完全表示源碼的情況,需要調(diào)試到匯編代碼或源碼查看).
我們可以通過終端進(jìn)入到要窺探所在文件的位置,使用命令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp.(如果電腦上面安裝了多個(gè)版本的Xcode,轉(zhuǎn)換為C++代碼的時(shí)候會(huì)提示各種框架找不到的錯(cuò)誤,一般是因?yàn)槎鄠€(gè)版本的Xcode路徑?jīng)_突導(dǎo)致的,我們需要在終端指定一個(gè)Xcode的路徑,例:sudo xcode-select --switch/Applications/Xcode10.0.app/Contents/Developer/).

注釋:解釋各種參數(shù)的翻譯
xc就是Xcode的縮寫名惩。
xcrun是Xcode的一種工具褐荷。
-sdk iphoneos規(guī)定sdk需要運(yùn)行在iOS系統(tǒng)上面。
clang是Xcode內(nèi)置的llvm編譯器前端台汇,也是編譯器的一種甲捏。
-arch xxx(arm64、i386景描、armv7...)指出iOS設(shè)備的架構(gòu)囱桨。
參數(shù) -rewrite-objc xx.m 是重寫objc代碼的指令(即重寫xx.m文件) 仓犬。
-o newFileName.cpp 表示輸出新的.cpp文件。

2. NSObject底層實(shí)現(xiàn)原理

NSObject底層原理.png

Class 定義為 :typedef struct objc_class *Class;也就是說Class是個(gè)結(jié)構(gòu)體指針.
代碼中[NSObject alloc]開辟空間給NSObject舍肠。obj的指針指向了isa的地址.isa的地址就是結(jié)構(gòu)體的地址,原因是結(jié)構(gòu)體的地址就是結(jié)構(gòu)體中第一個(gè)成員的地址,而結(jié)構(gòu)體只有一個(gè)成員,即isa指針的地址.

一. 例:student底層的原理

Student普通的結(jié)構(gòu).png

答:因?yàn)?code>Student繼承NSObject,也就繼承了NSObject的數(shù)據(jù)結(jié)構(gòu),所以繼承NSObject的8個(gè)字節(jié),也就是NSobject中的isa的大小搀继。

思考題:Student繼承Person的結(jié)構(gòu).png

Person占據(jù)class_getInstanceSize=16 malloc_size=16, Student占據(jù)class_getInstanceSize=16 malloc_size=16,Person的變量實(shí)際用了12,但是由于內(nèi)存對(duì)齊所以占用16.

二. 兩種方法看內(nèi)存大小

我們有這種方法在OC中表達(dá)一個(gè)類內(nèi)存的大小.

<objc/runtime.h>文件提供class_getInstanceSize(Class _Nullable cls)方法,返回我們一個(gè)OC對(duì)象
的實(shí)例所占用的內(nèi)存大小(可以說是結(jié)構(gòu)體內(nèi)存對(duì)齊之后的大小,8的倍數(shù))翠语;
<malloc/malloc.h>文件提供 size_t malloc_size(const void *ptr)方法返回系統(tǒng)為這個(gè)對(duì)象分配的
內(nèi)存大小(16的倍數(shù))叽躯。

三. 內(nèi)存對(duì)齊的原理(不全,后期添加)

我們先來看一些內(nèi)存的例子,更加方便我們?nèi)ダ斫鈨?nèi)存分配和內(nèi)存對(duì)齊原理:

  • 看一個(gè)沒有成員變量的類的實(shí)例(以NSObject為例)
    NSObject *obj = [[NSObject alloc] init];
    NSLog(@"NSObject實(shí)例大小--> %zd",class_getInstanceSize([obj class]));
    NSLog(@"obj實(shí)際分配的內(nèi)存%zd",malloc_size((const void *)obj));
//    NSObject實(shí)例大小--> 8
//    obj實(shí)際分配的內(nèi)存16
  • 一個(gè)普通的類的實(shí)例肌括,并且實(shí)例有且僅有唯一的成員變量(如:Student只有一個(gè)name屬性)
@interface Student: NSObject
@property (nonatomic, copy) NSString *name;
@end;

@implementation  Student
@end;

Student *stu = [[Student alloc] init];
stu.name = @"Object-C";
NSLog(@"Student實(shí)例大小--> %zd",class_getInstanceSize([stu class]));
NSLog(@"stu實(shí)際分配的內(nèi)存%zd",malloc_size((const void *)stu));
//     Student實(shí)例大小--> 16
//     stu實(shí)際分配的內(nèi)存16
  • 一個(gè)普通的類的實(shí)例点骑,并且實(shí)例有自己的成員變量(如:Student類,為其添加屬性age们童、name等)
@interface Student: NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end;

@implementation  Student
@end;

Student *stu = [Student new];
stu.name = @"Object-C";
stu.age = 25;
NSLog(@"Student實(shí)例大小--> %zd",class_getInstanceSize([stu class]));
NSLog(@"stu實(shí)際分配的內(nèi)存%zd",malloc_size((const void *)stu));
//     Student實(shí)例大小-->24
//     stu實(shí)際分配的內(nèi)存32

由以上三次測(cè)試:一個(gè)OC對(duì)象所占用的內(nèi)存取決于這個(gè)對(duì)象成員變量的多少.但是同時(shí),系統(tǒng)為其分配內(nèi)存時(shí),默認(rèn)會(huì)分配最少16個(gè)字節(jié)的大小.OC中對(duì)象的內(nèi)存小于16就等于16(是Core Foundation的規(guī)定),下面是Core Foundation的源碼.

size_t instanceSize(size_t extraBytes){
    size_t size = alignedInstanceSize()+extraBytes;
    //CF requires all objects be at least 16 bytes.
    if (size < 16) size = 16;
    return size;
}

內(nèi)存對(duì)齊的原則:結(jié)構(gòu)體的大小必須是最大成員的倍數(shù).
更多的內(nèi)存對(duì)齊的知識(shí)--內(nèi)存對(duì)齊
補(bǔ)充:sizeof不是個(gè)函數(shù)是個(gè)運(yùn)算符,傳入的時(shí)候是類型不是具體的對(duì)象,sizeof是在編譯的時(shí)候進(jìn)行計(jì)算的.

3. OC對(duì)象的分類

objective-C的對(duì)象,簡(jiǎn)稱為OC對(duì)象,分為三種:

  1. instance對(duì)象(實(shí)例對(duì)象).
  2. class對(duì)象(類對(duì)象).
  3. meta-class(元類對(duì)象).

一. 實(shí)例對(duì)象

NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];

object1畔况、object2是NSObject的instance對(duì)象(實(shí)例對(duì)象),它們是不同的兩個(gè)對(duì)象鲸鹦,分別占據(jù)著兩塊不同的內(nèi)存慧库。instance對(duì)象是通過類alloc出來的對(duì)象,每次調(diào)用alloc都會(huì)產(chǎn)生新的instance對(duì)象.instance對(duì)象在內(nèi)存中存儲(chǔ)的信息包括:isa指針,其他成員變量馋嗜。
實(shí)例對(duì)象存放的內(nèi)容包含:

實(shí)例對(duì)象
isa
成員變量信息

二. 類對(duì)象

NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = [NSObject class];
Class objectClass4 = object_getClass(object1);//Runtime API
Class objectClass5 = object_getClass(object2);//Runtime API

objectClass1 ~ objectClass5都是NSObject的class對(duì)象(類對(duì)象).它們是同一個(gè)對(duì)象,每個(gè)類在內(nèi)存中有且只有一個(gè)class對(duì)象.
類對(duì)象存放的內(nèi)容包含:

類對(duì)象
isa
superclass
屬性信息
對(duì)象方法信息
協(xié)議信息
成員變量信息
.............

class對(duì)象在內(nèi)存中存儲(chǔ)的信息主要包括:isa指針,superclass指針,類的屬性信息(@property)齐板、類的對(duì)象方法信息(instance method),類的協(xié)議信息(protocol)、類的成員變量信息(ivar).

三. 元類對(duì)象

獲取一個(gè)類對(duì)象的元類對(duì)象的方法.

Class objectMetaClass = object_getClass([NSObject class]);//Runtime API
元類對(duì)象
isa
superclass
類方法信息
.............

objectMetaClassNSObject的meta-class對(duì)象(元類對(duì)象).每個(gè)類在內(nèi)存中有且只有一個(gè)meta-class對(duì)象.
meta-class對(duì)象和class對(duì)象的內(nèi)存結(jié)構(gòu)是一樣的葛菇,但是用途不一樣甘磨,在內(nèi)存中存儲(chǔ)的信息主要包括:isa指針,superclass指針,類的類方法信息(class method).
補(bǔ)充:
查看Class是否為meta-class:

BOOL result = class_isMetaClass([NSObject class]);

以下代碼獲取的objectClass是class對(duì)象,并不是meta-class對(duì)象

Class objectClass = [[NSObject class] class];

objcget-Class和object-getClass區(qū)別

objc_getClass 傳入字符串類名返回類對(duì)象. 傳入字符串類名返回類對(duì)象. 傳入字符串類名返回類對(duì)象.
object_getClass 傳入實(shí)例對(duì)象返回類對(duì)象. 傳入類對(duì)象返回元類對(duì)象. 傳入元類對(duì)象返回還是元類對(duì)象

四. isa和superClass

1. isa
isa的指向關(guān)系圖.png

①instance的isa指向class,當(dāng)調(diào)用對(duì)象方法時(shí)眯停,通過instance的isa找到class济舆,最后找到對(duì)象方法的實(shí)現(xiàn)進(jìn)行調(diào)用.
②class的isa指向meta-class,當(dāng)調(diào)用類方法時(shí),通過class的isa找到meta-class莺债,最后找到類方法的實(shí)現(xiàn)進(jìn)行調(diào)用.

2. superClass
類對(duì)象的指向關(guān)系.png

當(dāng)Student的instance對(duì)象要調(diào)用Person的對(duì)象方法時(shí)滋觉,會(huì)先通過isa找到Student的class,然后通過superclass找到Person的class齐邦,最后找到對(duì)象方法的實(shí)現(xiàn)進(jìn)行調(diào)用.


元類對(duì)象的指向關(guān)系.png

當(dāng)Student的class要調(diào)用Person的類方法時(shí)椎侠,會(huì)先通過isa找到Student的meta-class,然后通過superclass找到Person的meta-class,最后找到類方法的實(shí)現(xiàn)進(jìn)行調(diào)用.

3. 經(jīng)典的isa和superclass圖譜
經(jīng)典圖片.png
  1. isa總結(jié)
  • instance的isa都是指向class.
  • class的isa都是指向meta-class.
  • meta-class的isa指向基類的meta-class.
  1. superClass總結(jié)
  • class的superClass指向父類的class.
  • 如果沒有父類,superClass指針為nil.
  • meta-class的superClass指向父類的meta-class.
  • 基類meta-class的superClass指向基類的class.
  1. instance的調(diào)用軌跡
  • isa找到class,方法不存在臂外,就通過superclass找父類.
  1. class調(diào)用類方法的軌跡
  • isa找meta-class季惩,方法不存在幅恋,就通過superclass找父類.
  • 基類的meta-class方法不存在,就通過superclass找基類的class,如果沒有找到就是nil.
4. isa地址運(yùn)算
isa.png
isa的MASK地址.png

從64bit開始辅肾,isa需要進(jìn)行一次位運(yùn)算盾戴,才能計(jì)算出真實(shí)地址,superClass存儲(chǔ)的地址值,直接就是父類的地址值,不用做位運(yùn)算.


一個(gè)對(duì)象完整的結(jié)構(gòu).png

實(shí)例對(duì)象里只有成員變量沒有方法,為什么實(shí)例對(duì)象的方法要存在類對(duì)象里,原因是只要存一份就夠了,實(shí)例對(duì)象會(huì)創(chuàng)建多個(gè).

                      想了解更多iOS學(xué)習(xí)知識(shí)請(qǐng)聯(lián)系:QQ(814299221)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末埠对,一起剝皮案震驚了整個(gè)濱河市仇冯,隨后出現(xiàn)的幾起案子之宿,更是在濱河造成了極大的恐慌,老刑警劉巖苛坚,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件比被,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡泼舱,警方通過查閱死者的電腦和手機(jī)等缀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來娇昙,“玉大人尺迂,你說我怎么就攤上這事∶罢疲” “怎么了噪裕?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)股毫。 經(jīng)常有香客問我膳音,道長(zhǎng),這世上最難降的妖魔是什么铃诬? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任祭陷,我火速辦了婚禮,結(jié)果婚禮上趣席,老公的妹妹穿的比我還像新娘兵志。我一直安慰自己,他們只是感情好宣肚,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布想罕。 她就那樣靜靜地躺著,像睡著了一般霉涨。 火紅的嫁衣襯著肌膚如雪按价。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天嵌纲,我揣著相機(jī)與錄音俘枫,去河邊找鬼。 笑死逮走,一個(gè)胖子當(dāng)著我的面吹牛鸠蚪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼茅信,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼盾舌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蘸鲸,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤妖谴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后酌摇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體膝舅,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年窑多,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了仍稀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡埂息,死狀恐怖技潘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情千康,我是刑警寧澤享幽,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站拾弃,受9級(jí)特大地震影響值桩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜砸彬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一颠毙、第九天 我趴在偏房一處隱蔽的房頂上張望斯入。 院中可真熱鬧砂碉,春花似錦、人聲如沸刻两。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽磅摹。三九已至滋迈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間户誓,已是汗流浹背饼灿。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留帝美,地道東北人碍彭。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親庇忌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子舞箍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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