題記
這系列的目的是回顧一下學到的知識,對OC底層原理作一翻探究,以及對一些常見的底層原理面試題作一次總結。
準備工作
-
如題目所示,我們新建一個demo來探究:一個NSObject對象占多大內存
如大部分iOS開發者所知,OC的底層是C/C++,為了清楚了解NSObject的本質,我們可以看看底層是怎么實現。
- 進入mian.m文件所在的路徑,打開terminal
- 輸入以下指令把OC代碼轉成C++(因為mac平臺下使用的是arm64架構,實際情況可以選擇相應的架構)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
-
我們可以得到一個main-arm64.cpp 文件
- 把cpp文件直接拽入Xcode,為了不顯示報錯信息,我們不讓它參與編譯
探究
-
點擊NSObject我們可以看到OC的定義
-
在main.cpp文件里面搜索NSObject_IMPL,我們可以看到NSObject轉成C++后的本質是一個結構體
- 我們再點擊Class進去,可以看到其實它就是一個指針。那么我們知道在64位環境下,它占8個字節。
論證
- 有了這個推論,我們還可以進一步論證我們的觀點,這里我們用到runtime,也就是運行時的一個方法class_getInstanceSize
- 為了驗證推論的準確性,我們先找到蘋果開源的源碼,打開瀏覽器
http://opensource.apple.com/tarballs/
搜索`objc
-
點擊進去,我們找到數字最大的一個,一般來說也是最新的一個
-
下載壓縮包解壓后并打開
- 搜索
class_getInstanceSize
,并在.mm文件里面可以看到它的實現
-
點擊3中的alignedInstanceSize()方法,我們可以看到進一步注釋,這個方法返回的是類對象成員變量所占用的內存大小
- 所以通過調用runtime的這個方法我們可以看到,NSObject類對象所占用的內存大小為8個字節
疑問
- 這個就是最終結果了嗎?不用急著下定義。我們還有個叫malloc_size的方法可以查看系統分配內存的大小。由于這里需要傳一個C的指針,所以我們打印obj的時候需要橋接一下。然后我們會發現,返回的大小是16而不是8!
總結
-
為什么會造成這個不一致呢?我們還是要從源碼入手分析,首先還是在objc的源碼里面搜索allocWithZone這個方法,查看創建一個對象是內存是怎么分配
- 我們點擊進入
class_createInstance
這個方法,看到返回的是_class_createInstanceFromZone
方法,我們再次點擊進去就能很快鎖定我們需要的信息:size
-
我們可以從注釋看到,所有對象至少分配16 bytes 的大小,從方法實現來看,如果size < 16,則size = 16