我们平常敲的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