窺探iOS底層實現--OC對象的本質(一) - 掘金數據結構
窺探iOS底層實現--OC對象的分類:instance、class、meta-calss對象的isa和superclass - 掘金框架
窺探iOS底層實現-- KVO/KVC的本質 - 掘金iphone
...函數
問題: 一個NSObject對象佔用多少個內存?
int main(int argc, char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc]init];
/// obj對象佔用了多少內存?
}
}
return 0;
複製代碼
平時咱們編寫的Objective-c的代碼,底層的實現其實都是C/C++的代碼。post
因此Objective-c 的面向對象都是基於C/C++的數據結構實現的。ui
思考問題: Objective-C的對象、類主要是基於C\C++的什麼數據結構實現的?
spa
///> 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++的代碼呢?
3d
終端進入到程序的目錄下:
輸入命令行
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
struct NSObject_IMPL {
Class isa; /// isa
};
複製代碼
NSObject的底層實現結構圖
上圖實際上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。
#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類的實例對象的大小
而後咱們用malloc_size的方法去查看obj指針指向內存的大小 爲16;
malloc_size 爲何是16接下來咱們能夠去查看源碼去解決問題: 源碼地址:Source Browser:OBJective-c源碼 找到objc4,下載版本號最大的就是最新的源碼去查看
alloc本質調用的是allocWithZone。 在源碼中搜索allocWithZone 在底層代碼中間咱們找到allocWithZone的底層方法。發現obj是由class_cerateInstance(cls,0)建立出來的。 而後咱們在進入_class_createInstanceFromZone(cls, extraBytes, nil); 進入後會看到calloc(1,size)的alloc的實現代碼,傳入了一個size,size是instanceSize(extraBytes)獲得的,咱們再次進入 規定對象的字節至少是16個字節, 當咱們的分配的size值小於16是 會把size設置爲16 咱們size傳進來的就是alignedInstanceSize() 就是咱們傳進來的實例變量的大小 爲8 因此當小於16的時候底層代碼中返回的就是16 , 因此分配的內存大小至少是16。一個NSObject對象佔用多少內存?
系統分配了16個字節給NSObject對象(經過malloc_size函數得到)
但NSObject對象內部只使用了8個字節的空間(64bit環境下,能夠經過class_getInstanceSize函數得到
#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;
}
複製代碼
思考: 若是個人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;
}
複製代碼
isa:佔用8個字節,_no:佔用4個字節,_age:佔用4個字節, _gender:佔用4個字節, 不該該是一共佔用了20個本身嗎?爲何是24個呢?
爲何會是24和32呢???? 窺探iOS底層實現--OC對象的本質(二) - 掘金