我們平常敲的Objective-C代碼,底層實現其實是C/C++代碼.那麼一個OC對象佔用多少內存.
iOS開發中Objective-C和swift用的是Clang/LLVM來編譯的.
使用的clang編譯器編譯成cpp,xcodebuild負責將OC/Swift工程編譯成xxx.app,xcrun負責給xxx.app簽名並打包成xxx.ipa
#import <Foundation/Foundation.h>
int main(int argc, char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
NSLog(@"hello world!");
}
return 0;
}
通過clang編譯器編譯成cpp,執行
clang -rewrite-objc main.m -o main.cpp
我們也可以指定iphoneos/arm64,執行
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main64.cpp
生成main64.cpp,其中main函數的執行代碼轉換成C/C++的代碼:
#pragma clang assume_nonnull end
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
/**
這裏我們可以看到objc_msgSend(id,SEL)
@param NSObject 類型是NSObject
@return 返回類型是NSObject
*/
NSObject *object = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_9d_y4q9htmj3q17ljhpchdzt5vc0000gp_T_main_2c6acf_mi_0);
}
return 0;
}
在這裏我們看到熟悉的面孔__autoreleasepool和objc_msgSend,一個是自動釋放池,一個是消息傳遞
自動釋放池內部用的是雙向鏈表,通過標記衛士來刪除
/**
這裏我們看到push入棧生成atautoreleasepoolobj
pop出棧生成atautoreleasepoolobj
*/
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
接着我們看到
//OC轉成C++的代碼
struct NSObject_IMPL {
Class isa;
};
typedef struct objc_class *Class;
struct objc_object {
Class _Nonnull isa __attribute__((deprecated));
};
// runtime.h
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
可以看到我們回到了NSObject就是一個對象,就是一個結構體指針
執行過程:objc_getClass-->(objc_class *)objc_getClass-->objc_class結構體
也就是class就是一個指針,指針在32位是4字節,64位是8字節
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
int main(int argc, char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
NSLog(@"hello world!");
//獲得NSobject對象實例成員變量佔用的大小 ->8
size_t size = class_getInstanceSize(obj.class);
//獲取NSObjet指針的指向的內存大小 ->16
size_t size2 = malloc_size((__bridge const void *)(obj));
NSLog(@"size;%zu size2:%zu",size,size2);
}
return 0;
}
執行結果:size;8 size2:16
也就是說obj就是一個指向class的指針,如果機器是64爲, obj指針是8字節,指針指向的內存大小爲16字節.
Person添加兩個成員變量
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
@interface Person : NSObject
{
@public
int _age;
int _no;
}
@end
int main(int argc, char * argv[]) {
@autoreleasepool {
// NSObject *obj = [[NSObject alloc] init];
// NSLog(@"hello world!");
Person *obj = [[Person alloc] init];
obj->_age = 15;
obj->_no = 14;
//獲取NSObject對象實例成員變量佔用大小 8
size_t size = class_getInstanceSize(obj.class);
//獲取NSObject指針指向的內存大小是 16
size_t size2 = malloc_size((__bridge const void *)(obj));
NSLog(@"size;%zu size2:%zu",size,size2);
}
return 0;
}
//轉爲C++
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;//8bytes
int _age; //4bytes
int _no; //4bytes
};
輸出結果:size;16 size2:16
實例對象其實是結構體,佔用內存是16倍數,實例對象大小不受方法影響,受實例成員變量多少影響
參考:https://juejin.im/post/5d15887ee51d45108126d28d