窺探iOS底層實現--OC物件的分類:instance、class、meta-calss物件的isa和superclass - 掘金
...
問題: 一個NSObject物件佔用多少個記憶體?
int main(int argc, char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc]init];
/// obj物件佔用了多少記憶體?
}
}
return 0;
複製程式碼
Objective-c的本質:
平時我們編寫的Objective-c的程式碼,底層的實現其實都是C/C++的程式碼。
![OC_%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84%E5%9B%BE.png](https://i.iter01.com/images/11c39f0d16b532487bad0b8631b62de863d58560c999847b46b523fa3480874b.png)
所以Objective-c 的物件導向都是基於C/C++的資料結構實現的。
思考問題: Objective-C的物件、類主要是基於C\C++的什麼資料結構實現的?
///> Student類
@interface Student: NSObject{
@public
int _no;
int _age;
}
@end
@implementation Student
@end
int main(int argc, char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc]init];
/// obj物件佔用了多少記憶體?
}
}
return 0;
複製程式碼
如果Objective-c的物件轉成C/C++的程式碼實際上最重轉成了C /C++的機構體。
那麼我們怎麼把OC的程式碼轉換成C/C++的程式碼呢?
-
終端進入到程式的目錄下:
-
輸入命令列
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC原始檔 -o 輸出的CPP檔案 eg: xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp 複製程式碼
-
然後機會生成一個main-arm64.cpp的檔案 這裡面就是我們的C/C++的實現。
-
如果需要連結其他框架,使用-framework引數。比如-framework UIKit
-
在生成main-arm64.cpp 內搜尋 NSObject_IMPL
- NSObject_IMPL: NSObject Implementation
- NSObject在記憶體中其實就是
///> NSObject_IMPL
struct NSObject_IMPL {
Class isa; /// isa
};
複製程式碼
NSObject的底層實現結構圖
![OC_NSObject_%E5%BA%95%E5%B1%82%E5%AE%9E%E7%8E%B0%E7%BB%93%E6%9E%84%E5%9B%BE.png](https://i.iter01.com/images/94ac16fa512097a27edf2d50b20424e123071e86a49d0e1c0870657cf5073a4a.png)
上圖實際上NSObject物件中存在一個isa指標,isa指標在64位系統中佔用8個位元組,在32位的系統中佔用4個位元組,目前用的是64位系統,所以在我們NSObject中isa指標會佔用8個位元組。CLass isa的內部實現為結構體。
/// class 其實就是一個指標 指向一個結構體的指標
typedef struct objc_class *Class
複製程式碼
/// 建立並分配儲存空間
NSObject *obj = [[NSObject alloc]init];
複製程式碼
假設我們NSObject物件分配了一塊儲存空間,假設之後8個位元組,在這8個位元組中我們只放了isa指標,假設我們的isa的地址為0x100400110,這個isa的地址就是結構體的地址。所以說obj的地址就是0x100400110。
NSObject佔用的記憶體
#import <malloc/malloc.h>
#import <OBJC/runtime.h>
///> main
int main(int argc, char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc]init];
///> 獲得NSObject類的例項物件的大小
NSLog(@"%zd", class_getInstanceSize([NSObject class]));
///> 獲取obj指標指向記憶體的大小
NSLog(@"%zd", malloc_size((__bridge const void *)obj));
/**輸出結果
8
16
*/
}
return 0;
}
複製程式碼
-
首先我們用的Runtime的 class_getInstanceSize()方法去檢視 NSObject類的例項物件的大小
- 傳入 類 class
- 注意: Instance 例項,返回一個類的例項大小佔用了記憶體空間的大小為8
-
然後我們用malloc_size的方法去檢視obj指標指向記憶體的大小 為16;
- 傳入obj的指標(會有錯誤提示 然後寫上橋接就好了(__bridge const void *) )
malloc_size 為什麼是16接下來我們可以去檢視原始碼去解決問題: 原始碼地址:Source Browser:OBJective-c原始碼 找到objc4,下載版本號最大的就是最新的原始碼去檢視
![OC_%E6%BA%90%E7%A0%81_objc4.png](https://i.iter01.com/images/5d070d3f469b0da1f2e772ef1afbf323a37b51e8d10705199593a3c719ee25f2.png)
![OC_malloc_size_01.png](https://i.iter01.com/images/413bff8ac3462f0451a6d7d6638bae5436b90f5906496c1d3fe666db87966406.png)
![OC_malloc_size_02.png](https://i.iter01.com/images/e97d59175b2c4473003ea6c76b7051daa19173b380dfbf623af9b0d90c8c655c.png)
![OC_malloc_size_03.png](https://i.iter01.com/images/35fdc083483707d99543c18fe911a790a78cd618ee8214cf7a1bedd4f2420baa.png)
![OC_malloc_size_04.png](https://i.iter01.com/images/d8e88f9a775fa9278f0399d66ebbc0a27b9869d5d338544237432c1546fd3eac.png)
一個NSObject物件佔用多少記憶體?
系統分配了16個位元組給NSObject物件(通過malloc_size函式獲得)
但NSObject物件內部只使用了8個位元組的空間(64bit環境下,可以通過class_getInstanceSize函式獲得
自定義NSObject 佔用的記憶體
#import <malloc/malloc.h>
#import <OBJC/runtime.h>
///> Student類
@interface Student: NSObject{
@public
int _no;
int _age;
}
///> 實際底層的結構體 結構
//struct Student_IMPL{
// Class isa,
// int _no,
// int _age;
//}
@end
@implementation Student
@end
///> main
int main(int argc, char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc]init];
Student *stu = [[Student alloc]init];
stu->_no = 4;
stu->_age = 5;
NSLog(@"no:%d, age:%d",stu->_no, stu->_age);
NSLog(@"%zd", class_getInstanceSize([Student class]));
NSLog(@"%zd", malloc_size((__bridge const void *)stu));
/**輸出結果
no:4, age:5
16
16
*/
}
return 0;
}
複製程式碼
- 當我們自定義一個NSObject的時候實際底層會有三個成員變數,isa指標佔用8個位元組,_no佔用4個位元組 _age真用4個位元組,所以我們最後的結果為 16,16
思考: 如果我的Student有三個成員變數 那麼會佔用對少個位元組?
class_getInstanceSize([Student class]) 的輸出是多少?
malloc_size((__bridge const void *)stu的輸出是多少?
#import <malloc/malloc.h>
#import <OBJC/runtime.h>
///> Student類
@interface Student: NSObject{
@public
int _no;
int _age;
int _gender;
}
///> 實際底層的結構體 結構
//struct Student_IMPL{
// Class isa,
// int _no,
// int _age;
// int _gender;
//}
@end
@implementation Student
@end
///> main
int main(int argc, char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc]init];
Student *stu = [[Student alloc]init];
stu->_no = 4;
stu->_age = 5;
stu->_gender = 0;
NSLog(@"%zd", class_getInstanceSize([Student class]));
NSLog(@"%zd", malloc_size((__bridge const void *)stu));
/**輸出結果
24
32
*/
}
return 0;
}
複製程式碼
- 最重的輸出結果為:
- class_getInstanceSize: 24
- malloc_size: 32
isa:佔用8個位元組,_no:佔用4個位元組,_age:佔用4個位元組, _gender:佔用4個位元組, 不應該是一共佔用了20個自己嗎?為什麼是24個呢?
為什麼會是24和32呢???? 窺探iOS底層實現--OC物件的本質(二) - 掘金
- 文章總結自MJ老師底層視訊。