[CH1-Q1] OC 對象的本質——一個NSObject實例對象占用多少內存?

*Q1:一個NSObject實例對象占用多少內存?*

NSObject 對于每一個iOS開發者來說都很熟悉,因為我們幾乎每時每刻都跟其打交道,但是我們可能不知道究竟這個熟悉的實例對象究竟占用我們內存的空間是多少呢?那么下面我們就一起來探討一下。

    NSObject *obj = [[NSObject alloc] init];

上面這段代碼,相信大家很熟悉,就是alloc分配內存給對象,調用init方法為父類屬性進行初始化,然后用指針obj指向我們剛才分配的地址。一個NSObject實例對象占用多少內存,其實就是在問這個obj指針指向的內存空間占用的內存空間是有多大?

要想解決這個問題:
①我們需要理解我們平常編寫的OC代碼其實就是基于C、C++為基礎而"面向對象"的一門語言。
②其次需要知道的是NSObject在內存中究竟是如何布局的?

下面我們先來看看第一個問題解釋:



我們日常編寫的OC代碼最終會在編譯器的作用下轉成C、C++語言、再到匯編語言、機器語言。通過下面的兩張圖可以發現OC代碼編寫的Student類和用C、C++代碼寫的strut結構體,有異曲同工之妙,那么我們是不是可以認為OC中的類底層代碼其實就是C、C++的struct(結構體)?



確實是如此,那我們有沒有方法去證明呢?答案是有的,我們可以通過新建一個Mac OS的命令行工程,在main函數中輸入:

    NSObject *obj = [[NSObject alloc] init];

然后打開我們的終端程序,輸入

    clang -rewrite-objc main.m -o main.cpp  或者
    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp 

我們通過使用xcode自帶的clang工具把OC代碼轉成C、C++代碼,那上面的兩個命令有什么不一樣的?我們都知道OC這門語言不單單只能開發iOS上面的應用程序,而且能夠開發MacOS、WatchOS等平臺上的一些程序,而第一條命令會把OC代碼轉換成支持所有平臺的C++程序,而第二條命令只會轉換成arm64架構的c++程序。之所以編譯成arm64架構支持的,是因為如今市面上的iPhone設備都使用該架構。

第一條命令和第二條命令生成的C++代碼我們把其拉到最底部,能夠發現我們main函數的代碼,然后通過搜索NSObject_IMPL找到 stuct NSObject_IMPL結構體。如下圖:

然后我們直接回到剛才編寫的OC命令行程序,按住Command鍵點入NSObject類,我們能夠看到我們NSObject其實就是下面這段代碼:

    @interface NSObject <NSObject> {
        #pragma clang diagnostic push
        #pragma clang diagnostic ignored "-Wobjc-interface-ivars"
            Class isa  OBJC_ISA_AVAILABILITY;
        #pragma clang diagnostic pop
    }

再做一個簡單的去除無用的代碼即可得到:

    @interface NSObject <NSObject> {
        Class isa;
    }

通過對比struct NSObject_IMPLNSObject內部OC代碼,我們可以證明OC中的類底層代碼其實就是C、C++的struct(結構體)。那回到我們一開始的問題:一個NSObject實例對象占用多少內存?我們可以發現struct NSObject_IMPL 中的isa成員變量其實是typedef struct objc_class *Class;這個種類型的指針。而我們知道一個類的地址是由他第一個成員變量的地址所決定的,也就是說如果isa在內存中的地址為 0x100400110那么這個NSObject實例對象的地址值就是0x100400110,因此我們能夠得出結論指向一個NSObject實例對象的指針obj的地址就是0x100400110

我們又知道一個指針在64位系統中占用的內存空間就是8個字節,那么我們可能會覺得只有一個isa指針的NSObject實例對象,它所占用的內存空間就是8個字節,其實這是不對的,其實一個NSObject實例對象占用的內存空間為16個字節,為什么呢?我們不妨通過兩個函數來驗證一下。通過使用<objc/runtime.h>中的class_getInstanceSize方法和<malloc/malloc.h>中的malloc_size方法來打印一下NSObject實例對象所占用的內存空間。

NSLog(@"class_getInstanceSize: %zd",class_getInstanceSize([NSObject class]));
NSLog(@"malloc_size: %zd",malloc_size((__bridge const void *)(obj)));

打印出來的分別是8和16,那為什么我們剛才說的是16個字節而不是8個字節呢?而且這兩個方法有什么區別呢?為什么是以malloc_size為標準呢?其實<objc/runtime.h>中的class_getInstanceSize方法所得到的是objc對象實際需要的內存大小,而<malloc/malloc.h>中的malloc_size方法所得到的是objc對象實際分配的內存大小。那為什么一個NSObject對象明明只需要8個字節的內存大小就可以了,但是還是分配到了16個字節大小的內存空間?對于這個問題我們可以通過閱讀objc4源碼來得到答案,地址https://opensource.apple.com/tarballs/ 。下載最新版本的objc4源碼。

通過跟蹤obj4中alloc和allocWithZone兩個函數的實現,會發現這個連個函數都會調用一個instanceSize的函數:

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

這個函數的代碼很簡單,返回的結果就是系統給一個對象分配內存的大小。當對象的實際大小小于16時,系統就返回16個字節的大小。也就是說16個字節大小是系統的最低消費。還是用坐車的例子來說明一下,假如有8個人想坐車,他們打電話叫車說要一輛能坐8個人大小的車,對方說sorry我們沒有坐8個人大小的車,我們這里最小的就是坐16個人的車。最后來了一輛坐16個人的車,拉了8個人開走了。車就好比一個NSOject對象,車上的乘客就好比是對象中的成員,車的大小或者說載客數量就相當于一個對象占用的內存大小,車上實際的乘客數量就是對象中成員的大小。所以說一個NSObject對象占用多少內存,我想應該很明白了。

總結:
    系統分配了16個字節給NSObject 對象(通過malloc_size獲得)
    但NSObject對象內部只使用了8個字節(64bit 通過class_getInstanceSize)
另外:
    我們可以通過view memory、lldb的指令去驗證我們的結論,這些操作會再后面制作的視頻講解中附上。
下一節:
    會繼續深入,推算針對我們自定義的類內存布局和對象占用的內存空間。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評論 6 540
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,275評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,904評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,633評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,368評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,736評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,919評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,481評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,235評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,427評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,656評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,055評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,348評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,160評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,380評論 2 379

推薦閱讀更多精彩內容