iOS底層原理探究- NSObject 所佔記憶體
物件導向的Objective-C
我們平時寫的 OC
程式碼底層實現為 C/C++
程式碼,因為 Runtime
讓 OC
具備了物件導向的特點,而後底層的 C/C++
會轉換成底層的 彙編 程式碼,最終被被解析成計算機能識別的 機器語言 。而 OC
中的類,正是正是基於 C/C++
的結構體實現的。我們可以通過 clang
命令將我們平時所寫的 OC
程式碼轉換為 C/C++
程式碼。這是轉換程式碼:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 原始檔名 -o 目標檔名
複製程式碼
如果需要連結其他框架,使用 -framework引數
比如 -framework UIKit
如: 我們進入原始檔所在的目錄,執行 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
會在當前目錄生成一個main.cpp的檔案,這就是一個最簡單的OC檔案的 C++
實現。
通過轉換之後我們很容易找到 NSObject
類的真正實現:
struct NSObject_IMPL {
Class isa;
};
複製程式碼
只有一個 名為 isa
的 Class
例項。繼續探尋 Class
的宣告:
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
複製程式碼
發現 Class
實際上是一個指向 objc_class
的結構體指標。
也就是說 NSObject
最終宣告為一個 指向結構體 objc_class
的名為 isa
的結構體指標。既然是指標,在32位系統中佔 4個 位元組,在64位系統中佔 8 個位元組。如何檢視自己的Mac是多少位呢?可以在終端 輸入 uname -a
若末尾顯示 x86_64 則代表是64位系統,如果末尾顯示 i686 則代表是32 系統。目前我們所使用的大多都是 64 位。
使用 Runtime 中 class_getInstanceSize
輸出一個類的例項物件的成員變數的大小
NSObject *obj = [[NSObject alloc] init];
NSLog(@"%zd",class_getInstanceSize([obj class]));
複製程式碼
輸出:
由此可見,一個 NSObject 只有一個成員變數 即 isa,它所佔 8 個位元組的大小。 我們可通過 objc4原始碼得出此結論。
Class's ivar size rounded up to a pointer-size boundary. 一個類的所有成員變數所佔用的空間。
那在 OC
中一個 NSObject
物件佔用8個位元組嗎?答案是否定的,我們繼續分析。
使用 malloc_size(const void *ptr)
輸出一個類實際分配的記憶體大小
NSObject *obj = [[NSObject alloc] init];
NSLog(@"%zd",malloc_size((__bridge const void *)obj));
複製程式碼
輸出:
2018-05-29 07:40:24.270554+0800 XWTest0[25108:1196172] 16
複製程式碼
此外我們也可以用這種方式來證明一個NSObject物件佔 16 個位元組
我們知道在 OC
的物件例項中, 真正給物件分配記憶體的方式是
+ (instancetype)allocWithZone:(struct _NSZone *)zone OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
複製程式碼
我們可以探究 allocWithZone
方法的底層實現, 同樣檢視Apple開源的OC原始碼 objc4原始碼