01 iOS底層原理 - OC對象本質(zhì)探究

廢話不多說,要了解OC對象的本質(zhì),先要明確一點,都有哪些是屬于OC的對象:實例對象,類對象,元類對象。

一,Object-C對象

1. instance (實例對象)

即alloc出來的對象,都會有單獨自己的內(nèi)存地址。
實例對象存儲的信息主要有:

  • isa指針
  • 其他成員變量
2. class(類對象)

每個類在內(nèi)存中有且只有一個class對象。
類對象存儲的信息主要有:

  • isa指針
  • superclass指針
  • 類屬性信息、類的實例對象方法信息(值存儲 -()xxx 這樣的方法)
  • 類協(xié)議信息、類成員變量信息(變量的類型和名稱,不存儲具體的賦值)
3. meta-class(元類對象)

每個類在內(nèi)存中有且只有一個meta-class對象。
meta-class對象和class對象的內(nèi)存結(jié)構(gòu)是一樣的,但是用途不一樣,在內(nèi)存中存儲的信息主要包括

  • isa指針
  • superclass指針
  • 類的類方法信息(class method +()xxx)

二,如何分析

大家都知道,OC最后要執(zhí)行,都會轉(zhuǎn)成機器語言(二進制),但是在這個過程中,還做了哪些轉(zhuǎn)換呢?
其實,OC到機器語言,中間還經(jīng)歷了c/c++、匯編語言,最后才到機器語言的。具體看看下面這個圖:

image.png

在這里我們主要分析的是:OC到c/c++這一層的轉(zhuǎn)換,看看oc的類、對象所占內(nèi)存、系統(tǒng)分配內(nèi)存、及內(nèi)存信息等
具體怎么操作呢?

1. 按照圖示,將工程目錄cd 到終端
image.png

第一個紅框就是程序的main函數(shù),程序的主入口

2. 在終端上輸入
clang -rewrite-objc main.m -o main.cpp

表示重寫main.m文件,最后輸出為main.cpp文件

image.png

main.cpp中就是最終轉(zhuǎn)換成C/C++的代碼,只不過這種轉(zhuǎn)換是全平臺的轉(zhuǎn)( win、iphone、Mac),我們并不需要所有的平臺只需要iphone即可

3. 可以在終端敲入下面命令,可以生成只支持ios平臺的c/c++代碼
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp

表示在xcode工具下運行 iphone平臺,編譯arm64架構(gòu),重寫 mai.m文件,輸出到 main-arm64.cpp文件
ios上的架構(gòu):模擬器(i386),32bit(armv7),64bit(arm64)

image.png

三,OC對象本質(zhì),具體分析

1. 先看看NSObject 轉(zhuǎn)成 C/C++后,長啥樣了

按照下圖所示,找到相應(yīng)代碼,發(fā)現(xiàn),這哥們最后變成了一個結(jié)構(gòu)體
在這個文件里面搜所,大家會發(fā)現(xiàn),好多基于NSObject的結(jié)構(gòu)體,這就說明
oc的對象、類主要是基于C/C++的結(jié)構(gòu)體實現(xiàn)的

struct NSObject_IMPL {
    Class isa; // 這是一個指針,內(nèi)存大小是8個字節(jié)
};
image.png
2. 看一道面試題

面試題:
一個 NSObject 對象占用多少內(nèi)存?
可以理解為,它本身占了多少內(nèi)存,系統(tǒng)給分配了多少內(nèi)存
答案:系統(tǒng)分配了16個字節(jié)給NSObject對象(通過malloc_size函數(shù)獲得),但是NSObject內(nèi)部只使用了8字節(jié)空間(通過class_getInstanceSize函數(shù)獲得)

我們已經(jīng)知道了oc對象、類是基于c/c++的結(jié)構(gòu)體數(shù)據(jù)類型實現(xiàn)的
所以,NSObject本身至少占8字節(jié)的內(nèi)存,但是實際占多少呢,系統(tǒng)又給分配多少呢,看下面的例子

// 導(dǎo)入頭文件
// #import <objc/runtime.h>
// #import <malloc/malloc.h>

        NSObject *obj = [[NSObject alloc]init];
        // 獲取NSObject類的實例對象的成員變量所占用的大小,就是真正利用的 8 字節(jié)
        // Class's ivar size rounded up to a pointer-size boundary.
        NSLog(@"%zd", class_getInstanceSize([NSObject class]));

        // 獲取obj所指向內(nèi)存的大小,就是系統(tǒng)分配的有 16 字節(jié)
        // 搜索 allocWithZone 可以查看
        // CF requires all objects be at least 16 bytes.
        NSLog(@"%zd", malloc_size((__bridge const void *)(obj)));

從上面的例子中我們可以看出,可以通過兩個函數(shù)來獲取實際占用內(nèi)存和系統(tǒng)分配內(nèi)存的大小
可以在 https://opensource.apple.com/tarballs/objc4/ 查看 objc的源碼, 就知道了 class_getInstanceSize 和 malloc_size 的本質(zhì)

四,總結(jié)

OC對象、類是基于c/c++的設(shè)么數(shù)據(jù)結(jié)構(gòu)實現(xiàn)的

主要是基于結(jié)構(gòu)體實現(xiàn)的,這個可以通過查看源碼驗證
只要大家抓住這個本質(zhì),就會很容易分析出來一個實例對象到底站用多少內(nèi)存,存儲的信息都有哪些

思考:如果創(chuàng)建一個基于NSObject的類,里面有幾個成員變量,再來分析一下內(nèi)存結(jié)構(gòu),看看有什么不同的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容