OC對(duì)象的本質(zhì)

OC和C_C++OC和C_C++

  • 一個(gè)NSObject對(duì)象占多少內(nèi)存
        NSObject *person = [[NSObject alloc] init];

也就是說person指針指向的這段內(nèi)存空間,占有多少內(nèi)存空間?
要想知道他在內(nèi)存中占有多少空間转培,就要知道他在內(nèi)存是怎么布局的恶导,在內(nèi)存中包含哪些內(nèi)容,搞清楚這段代碼的本質(zhì)是什么浸须,
我們平時(shí)編寫的OC代碼惨寿,底層實(shí)現(xiàn)其實(shí)都是C\C++代碼,

截屏2020-10-18 下午6.45.28.png

編譯器會(huì)將C\C++轉(zhuǎn)成匯編,然后再轉(zhuǎn)成機(jī)器語言運(yùn)行
所以O(shè)bjective-C的面向?qū)ο蠖际腔贑\C++的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的
思考:Objective-C的對(duì)象删窒、類主要是基于C\C++的什么數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的裂垦?
結(jié)構(gòu)體

將Objective-C代碼轉(zhuǎn)換為C\C++代碼

可以安裝一個(gè)gotocell可以快速定位到終端
代碼之后之間的轉(zhuǎn)換肯定是編譯器編譯的結(jié)果,所以要用編譯器相關(guān)的工具肌索,這里使用使用的clang缸废,clang是xcode內(nèi)置的編譯器llvm的編譯器前端
clang -rewrite-objc main.m -o main.cpp
因?yàn)槲覀兩晌募莄和c++都有的所有最好生成CPP文件(c plus plus),
不建議直接用上面的來直接轉(zhuǎn),因?yàn)榫幾g也要看我們要轉(zhuǎn)成什么平臺(tái)的代碼驶社,不同平臺(tái)支持的代碼肯定是不一樣的企量,因?yàn)槲覀兊拇a會(huì)轉(zhuǎn)成匯編,會(huì)變得運(yùn)行要根據(jù)硬件不同來運(yùn)行亡电,所以我們只希望生成IOS平臺(tái)來生成届巩,
模擬器(i386) 32bit(armv7),64bit(arm64)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 輸出的CPP文件
沒有指定架構(gòu)的生成文件的大小3.5M,指定之后只有1M多份乒,

截屏2020-10-18 下午7.11.04.png

NSObject對(duì)象的內(nèi)存本質(zhì)

我們上面生成文件恕汇,就是想看看 [[NSObject alloc] init];他的本質(zhì)是什么腕唧,
我們?cè)傧嚓P(guān)的文件中可以找到NSObject_IMPL

struct NSObject_IMPL {
    Class isa;
};

如果將我們cpp拖進(jìn)xcode項(xiàng)目,編譯會(huì)報(bào)錯(cuò)瘾英,因?yàn)閏pp是臨時(shí)生成的枣接,還有一個(gè)就是cpp文件也有一個(gè)main函數(shù),一個(gè)程序只能有一個(gè)main函數(shù)缺谴,所有也會(huì)出錯(cuò)但惶,
可以再Xcode的編譯文件中,將cpp去掉湿蛔,


截屏2020-10-18 下午7.18.34.png

NSObject_IMPL他的意思就是NSObject Implementation膀曾,也就是一個(gè)NSObject的底層實(shí)現(xiàn),
如果我們直接通過Xcode點(diǎn)進(jìn)去看一下NSObject的實(shí)現(xiàn)可以發(fā)現(xiàn)

@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
@end
簡(jiǎn)化成
@interface NSObject {
    Class isa  ;
}
@end

OC的類他的底層實(shí)現(xiàn)就是結(jié)構(gòu)體阳啥,C\C++的結(jié)構(gòu)體支撐的OC的面向?qū)ο?/p>

  • 問題一個(gè)OC對(duì)象內(nèi)存中是如何布局的添谊?
    NSObject的底層實(shí)現(xiàn),頭文件里面


    截屏2020-10-18 下午7.26.57.png

    底層實(shí)現(xiàn)


    截屏2020-10-18 下午7.34.12.png

    isa 我們可以按進(jìn)去看一下,他的類型是這個(gè)樣子,是一個(gè)指向結(jié)構(gòu)體的指針
typedef struct objc_class *Class;

既然isa是一個(gè)指針那么他在64bit的機(jī)器占據(jù)8個(gè)字節(jié)察迟,32bit4個(gè)字節(jié)斩狱,
所以alloc相當(dāng)于給右邊的結(jié)構(gòu)體分配存儲(chǔ)空間,分配完成之后扎瓶,會(huì)又一個(gè)指針所踊,指向我們的分配的這段空間,假設(shè)我們分配的空間isa的地址0x100400110


截屏2020-10-18 下午7.40.03.png

class_getInstanceSize栗弟、malloc_size.

一個(gè)NSObject對(duì)象占多少內(nèi)存?
根據(jù)我們上面代碼分析工闺,我們可能會(huì)覺得一個(gè)NSObject占用8個(gè)字節(jié)的內(nèi)存乍赫,實(shí)際上不是,實(shí)際上是16個(gè)字節(jié)陆蟆,但是他利用起來的只有8個(gè)字節(jié)的大小雷厂,我們可以用runtime來驗(yàn)證一下,
class_getInstanceSize用來獲取一個(gè)類的實(shí)力對(duì)象的大小叠殷,實(shí)例就是我們通過alloc出來的具體對(duì)象改鲫,
malloc_size獲得指針?biāo)赶虻膬?nèi)存大小

       //獲得NSObject的實(shí)例對(duì)象的成員變量所占用的大小
       NSLog(@"%zu大小", class_getInstanceSize([NSObject class]));//8字節(jié)
       //獲得指針?biāo)赶虻膬?nèi)存大小
       NSLog(@"%zd", malloc_size(CFBridgingRetain(person)));// 16字節(jié)

蘋果開源網(wǎng)站 opensource.apple.com->objc(https://opensource.apple.com/tarballs/objc4/)找到最新的進(jìn)行下載
解壓之后我們直接打開項(xiàng)目,搜索class_getInstanceSize查看源碼發(fā)現(xiàn)林束,他會(huì)調(diào)用一個(gè)

截屏2020-10-18 下午8.06.33.png

返回就是實(shí)例對(duì)象的成員變量的大小
實(shí)際內(nèi)存中
截屏2020-10-18 下午8.10.15.png

面試題回答

我們還是回到IOS的源碼
通過alloc查看他是否真的是占用了16個(gè)字節(jié)像棘,實(shí)際上調(diào)用了allocWithZone



截屏2020-10-18 下午8.14.56.png

在它里面調(diào)用了calloc函數(shù),這里需要傳一個(gè)size參數(shù),


size

所以一旦發(fā)現(xiàn)小于16他就會(huì)分配16個(gè)字節(jié)壶冒,因?yàn)樗?guī)定所有的對(duì)象最低是16個(gè)字節(jié)缕题,這是corefoundation硬性規(guī)定的

  • 一個(gè)NSObject對(duì)象占多少內(nèi)存?

系統(tǒng)分配了16個(gè)字節(jié)給NSObject對(duì)象(通過malloc_size函數(shù)獲得)
但NSObject對(duì)象內(nèi)部只使用了8個(gè)字節(jié)的空間(64bit環(huán)境下胖腾,可以通過class_getInstanceSize函數(shù)獲得)

窺探NSObject的內(nèi)存

我們可以從另一個(gè)角度去驗(yàn)證他是否占了16個(gè)字節(jié)的內(nèi)存,Xcode工具烟零,打斷點(diǎn)瘪松,查看對(duì)象的地址


截屏2020-10-18 下午8.32.19.png

然后通過debug->debugworkflow->view memory


截屏2020-10-18 下午8.33.49.png

因?yàn)閮?nèi)存使用的是16進(jìn)制,所以兩位占一個(gè)字節(jié)锨阿,
通過內(nèi)存圖我們可以看出來我們的對(duì)象在內(nèi)存的分布形式,有顏色的部分就是我們的NSObject的內(nèi)存分布宵睦,可以看出來,他只是用了前8個(gè)字節(jié)

內(nèi)存分布16.png

我們也可以通過打斷點(diǎn)之后使用(lldb)調(diào)試器來進(jìn)行調(diào)試

lldb常用的指令
print墅诡、p:打印

po:打印對(duì)象
內(nèi)存讀取
memory read/數(shù)量格式字節(jié)數(shù) 內(nèi)存地址
x/數(shù)量格式字節(jié)數(shù) 內(nèi)存地址
x/3xw 0x10010

  • 格式
    x是16進(jìn)制壳嚎,f是浮點(diǎn),d是10進(jìn)制

     字節(jié)大小
     b:byte 1字節(jié)书斜,h:half word 2字節(jié)
     w:word 4字節(jié)诬辈,g:giant word 8字節(jié)
    
  • 修改內(nèi)存中的值
    memory write 內(nèi)存地址 數(shù)值
    memory write 0x0000010 10

使用例子


截屏2020-10-18 下午8.55.29.png

Student的本質(zhì)

@interface Student : NSObject{
    @public
    int _no; //4字節(jié)
    int _age;//4字節(jié)
    
}
@end

@implementation Student
@end

通過終端生成C++代碼

struct NSObject_IMPL
{
    Class isa; //8字節(jié)
};
struct Student_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _no;
    int _age;
};

我們寫的創(chuàng)建一個(gè)對(duì)象的方法

        Student *student = [[Student alloc] init];
        NSLog(@"%zd", class_getInstanceSize([student class]));//16
        NSLog(@"%zd", malloc_size((__bridge const void *)(student)));//16
        student->_no = 12;
        student->_age = 15;

在內(nèi)存中的分布圖其實(shí)是這個(gè)樣子的


student對(duì)象的內(nèi)存結(jié)構(gòu)

因?yàn)榻Y(jié)構(gòu)體的地址就是第一個(gè)成員變量的地址,所以Student的地址就是isa的地址荐吉,內(nèi)存分布是連續(xù)的焙糟,接下來的4個(gè)字節(jié)存儲(chǔ)的_no的值,再4個(gè)是age的值样屠,
我們可以強(qiáng)制將student指針穿撮,轉(zhuǎn)成結(jié)構(gòu)體類型


        struct Student_IMPL *stu = (__bridge struct Student_IMPL *)student;
        NSLog(@"no=%d,age=%d", stu->_no,stu->_age);//no=12,age=15

也可以正常的訪問,進(jìn)一步說明了痪欲,他本質(zhì)上就是這個(gè)結(jié)構(gòu)體類型

Student的內(nèi)存結(jié)構(gòu)

CPU從內(nèi)存讀取數(shù)據(jù)的方式分為大端和小端模式悦穿,IOS就是小端模式,讀數(shù)據(jù)會(huì)從小的地址開始业踢,所以他的四個(gè)字節(jié)是 04 00 00 00栗柒,

更復(fù)雜的集成模式

//Person
@interface Person:NSObject
{
    @public
    int _no;
}
@end

@implementation Person

@end
//Student
@interface Student:Person
{
    @public
    int _age;
}
@end
  • 思考:一個(gè)Person對(duì)象、一個(gè)Student對(duì)象占用多少內(nèi)存空間知举?


    截屏2020-10-19 上午11.12.49.png

    答案 都是16個(gè)字節(jié)

內(nèi)存對(duì)其瞬沦,結(jié)構(gòu)的內(nèi)存大小,必須是最大成員變量的倍數(shù)雇锡,

因?yàn)镻erson占居了16個(gè)字節(jié)逛钻,但是最后面的4個(gè)字節(jié)是空的所以當(dāng)Student繼承了之后,會(huì)把自己age放到最后面的4個(gè)空字節(jié)上面锰提,因此曙痘,student還是占據(jù)了16個(gè)字節(jié),

如果我們?cè)黾右粋€(gè)int類型的成員變量立肘,就會(huì)占32边坤,

@interface Student:Person //16
{
    @public
    int _age;//4
    int _weight;//4
}
@end

我們通過alloc init出來的實(shí)例對(duì)象,是不會(huì)存儲(chǔ)方法的谅年,他只存有成員變量的值惩嘉,因?yàn)榉椒ㄔ趦?nèi)存中指存在一份就夠了,不需要每個(gè)實(shí)例都存一份

回答疑問

計(jì)算內(nèi)存地址踢故,就是一個(gè)個(gè)的往后數(shù)文黎,

12-內(nèi)存分布注意點(diǎn)

@interface Dog : NSObject
{
    //struct NSObject_IMPL NSObject_IVARS;//8個(gè)字節(jié)
    int _no;//4
    int _age;//4
    int _height;//4
} //理論上他在內(nèi)存的大小是24字節(jié)
NSLog(@"%zd", class_getInstanceSize([dog class]));//24
NSLog(@"%zd", malloc_size((__bridge const void *)(dog)));//32

因?yàn)閙alloc_size結(jié)果是32 惹苗,這個(gè)是他在內(nèi)存中被分配的大小,因?yàn)樗麅?nèi)存對(duì)其的單位是16耸峭,所以malloc的空間必須是16的倍數(shù).

13-alloc的size分析

在calloc函數(shù)中分配內(nèi)存的時(shí)候桩蓉,傳入的size確實(shí)就是實(shí)力對(duì)象的內(nèi)存大小,24劳闹,但是底層內(nèi)部的實(shí)現(xiàn)院究,會(huì)將這個(gè)歌內(nèi)存驚醒對(duì)其計(jì)算,返回一個(gè)對(duì)其之后的size在進(jìn)行實(shí)際分配本涕,對(duì)其之后就是32

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末业汰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子菩颖,更是在濱河造成了極大的恐慌样漆,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晦闰,死亡現(xiàn)場(chǎng)離奇詭異放祟,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)呻右,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門跪妥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人声滥,你說我怎么就攤上這事眉撵。” “怎么了落塑?”我有些...
    開封第一講書人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵纽疟,是天一觀的道長。 經(jīng)常有香客問我芜赌,道長仰挣,這世上最難降的妖魔是什么伴逸? 我笑而不...
    開封第一講書人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任缠沈,我火速辦了婚禮,結(jié)果婚禮上错蝴,老公的妹妹穿的比我還像新娘洲愤。我一直安慰自己,他們只是感情好顷锰,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開白布柬赐。 她就那樣靜靜地躺著,像睡著了一般官紫。 火紅的嫁衣襯著肌膚如雪肛宋。 梳的紋絲不亂的頭發(fā)上州藕,一...
    開封第一講書人閱讀 52,328評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音酝陈,去河邊找鬼床玻。 笑死,一個(gè)胖子當(dāng)著我的面吹牛沉帮,可吹牛的內(nèi)容都是我干的锈死。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼穆壕,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼待牵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起喇勋,我...
    開封第一講書人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤缨该,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后茄蚯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體压彭,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年渗常,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了壮不。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡皱碘,死狀恐怖询一,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情癌椿,我是刑警寧澤健蕊,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站踢俄,受9級(jí)特大地震影響缩功,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜都办,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一嫡锌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧琳钉,春花似錦势木、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽划鸽。三九已至凉馆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間且改,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來泰國打工板驳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留钾虐,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓笋庄,卻偏偏與公主長得像效扫,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子直砂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359