OC的底層實現
OC代碼底層實現都是由OC編譯成C/C++,然后再編譯成匯編語言最后轉變成機器語言。所以由此可見,OC面向對象都是基于C/C++的數據結構實現的。
OC對象是基于C/C++的什么數據結構實現的?
NSObject *objc = [[NSObject alloc] init];
也就是說當我們創建一個新的NSObject對象的時候,在底層C語言或者C++這一層,objc這個對象是以一種什么數據類型存在的?
Xcode創建一個新項目,在macOS下->Command Line Tool
在Main.m中創建一個NSObject對象:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSObject *objc = [[NSObject alloc] init];
}
return 0;
}
生成了一個NSObject的實力對象objc,然后在利用Go2Shell打開重點,輸入:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
回車之后我們會得到由OC語言轉化為C++語言的一個文件,main.cpp(main.c plus plus)
打開這個文件,搜索NSObject,可以找到有個NSObject_IMPL,這個就是NSObject的實現
struct NSObject_IMPL {
Class isa;
};
由此可知,main.m里這個objc對象在C++底層中是以struct的形式存在的,其實對于任何一個類的實例,因為所有類都是繼承NSObject其在底層實現都是以結構體形式存在的,實際上結構體這種數據類型來實現類是比較合適的方式。
繼承類的實現
如果有一個類Student繼承于NSObject類,那么他在底層中是如何實現的呢?
同樣按照NSObjct分析方式,創建Student類之后再講main.m轉換為main.cpp之后可以看到Student的實現方式:
struct Student_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
int _no;
};
在這里,還聲明了兩個實例變量,_no和_age,可以看到Student在底層中的實現也是結構體形式,但是其里面除了_no和_age兩個實例變量以外還有個struct NSObject——IMPL NSObject_IVARS類型,其實這個類型就是指的Student父類NSObject,他在子類實現中是以成員變量形式存在的。
一個NSObject對象占用多少內存?
基于以上分析,下面來討論一個NSObject對象占用多少內存,看這個問題之前先看下基本知識:
現在基本都是64位機器,以64位機器為例,在OC中int占4個字節,NSInteger占8個字節。
除此之外OC中還有兩個函數:
class_getInstanceSize(<#Class _Nullable __unsafe_unretained cls#>);
malloc_size(<#const void *ptr#>)
這兩個函數一個是在runtime下的一個是malloc下的,getInstanceSize函數是獲取實例的大小,malloc_size函數作用是創建一個實例對象實際分配多少內存,兩個函數還是有區別的:
NSObject *objc = [[NSObject alloc] init];
NSLog(@"創建NSObject實例所需要的內存空間為:%zd",class_getInstanceSize([NSObject class]));
NSLog(@"創建NSObject實例所實際占用的內存空間為:%zd",malloc_size((__bridge const void *)objc));
輸出結果是不一樣的,一個是8個字節,一個是16個字節
2020-02-28 21:02:47.621864+0800 OC本質[74589:1018891] 創建NSObject實例所需要的內存空間為:8
2020-02-28 21:02:47.622331+0800 OC本質[74589:1018891] 創建NSObject實例實際占用的內存空間為:16
再來打印一下Student類通過兩個函數所占用空間:
@interface Student : NSObject
{
int _age;
int _no;
double _height;
}
NSLog(@"創建NSObject實例所需要的內存空間為:%zd",class_getInstanceSize([Student class]));
NSLog(@"創建NSObject實例所實際占用的內存空間為:%zd",malloc_size((__bridge const void *)student));
2020-02-28 21:28:50.623604+0800 OC本質[75502:1041829] 創建NSObject實例所需要的內存空間為:24
2020-02-28 21:28:50.623998+0800 OC本質[75502:1041829] 創建NSObject實例所實際占用的內存空間為:32
通過打印結果可以看到,Student有三個成員變量,一個打印結果是24字節,一個是32字節,那么這兩個區別到底在哪里呢?
查看runtime源碼可以得出結論,class_getInstanceSize其實際是指的對象申請開辟的空間,即我有多少實例,占用多少字節那就申請多少字節空間,上面例子中,Student有三個實例變量
_age int類型 4個字節
_no int類型 4個字節
_height double類型 8個字節
所以加起來是16個字節,除此之外還有父類NSObject在底層也是在Student成員變量里面的又是8個字節,所以加起來實際需要24個字節,那么就申請24個字節。
那malloc_size中的32字節是怎么來的呢?
malloc_size指的是系統實際開辟的字節,明明只申請24字節,卻開辟32字節,是因為OC中有內存對齊規則的,簡單說就是申請的字節空間就是由我所有的成員變量加起來所需要的空間,但是系統真正開辟的空間是比申請空間大的數的對16的最小公倍數,比如實際需要24字節,但是24不是16的整數倍,那么大于24字節又是16整數倍的就是32了,所以系統開辟了32字節。
內存對齊規則
1.結構體內成員按自身按自身長度自對齊。
自身長度,如char=1,short=2,int=4,double=8,。所謂自對齊,指的是該成員的起始位置的內存地址必須是它自身長度的整數倍。如int只能以0,4,8這類的地址開始
2.結構體的總大小為結構體的有效對齊值的整數倍
OC中分配內存總結
對象內存的申請按照8字節對齊,不滿16字節按照16字節計算;但是實際上calloc實際開辟內存的時候,則是進行了16字節對齊.
關于內存對齊詳細解釋參考:
http://www.lxweimin.com/p/a57a152232f2