NSObject的本质
先创建个demo,方便调试
编译过程:OC — C++ — 汇编语言 — 机器码;
main.m中,创建一个NSObject对象:
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
}
return 0;
}
命令行将main.m转成c++代码:
没有指定架构,生成main.cpp文件(3.7M)
clang -rewrite-objc main.m -o main.cpp
指定arm64架构,生成main-arm64.cpp文件(1.8M)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64
.cpp
在main-arm64.cpp中搜索NSObject,找到NSObject_IMPL,IMPL-implementation(实现)
struct NSObject_IMPL {
Class isa;
};
可见NSObject本质就是NSObject_IMPL的struct(结构体);
结构体里只有一个指向Class的isa指针,64位系统一个指针占8个字节(32位4个)
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
//获取NSObject实例对象大小:64位 占8个字节
NSLog(@"%zd",class_getInstanceSize([NSObject class])); //8
}
return 0;
}
#import <malloc/malloc.h> //引入头文件
int main(int argc, const char * argv[]) {
@autoreleasepool {
//获取NSObject实例对象大小(实际占用的内存大小):64位 占8个字节
NSLog(@"%zd",class_getInstanceSize([NSObject class])); //8
NSObject *obj = [[NSObject alloc] init];
//obj指针所指向的内存大小(堆分配的内存大小)
NSLog(@"%zd",malloc_size((__bridge const void *)obj)); //16
}
return 0;
}
🌰
创建继承NSObject的Person类:
@interface Person() {
int _age;
int _height;
int _weight;
}
@end
同理转成c++文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person.m -o Person.cpp
查看Person的内存结构,父类的NSObject_IMPL占8个字节,int各占4个字节
struct NSObject_IMPL {
Class isa; //8
};
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS; //8字节
int _age; //4
int _height; //4
int _weight; //4
};//20个字节,内存对齐,应该是8的倍数,所以最终占24个字节
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
//结构体所占大小
NSLog(@"%zd",sizeof(struct Person_IMPL)); //24
//对象实际大小
NSLog(@"%zd",class_getInstanceSize([Person class])); //24
}
return 0;
}
接下来我们看个更复杂的🌰🌰
Dog继承自Animal,Animal继承自NSObject
@interface Animal : NSObject
{
int _age;
}
@end
@implementation Animal
@end
@interface Dog : Animal
{
int _no;
}
@end
@implementation Dog
@end
转成c++,可以发现类对象是以结构体的形式存储在内存中的
struct NSObject_IMPL {
Class isa; //8
};
struct Animal_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age; //4
};
struct Dog_IMPL {
struct Animal_IMPL Animal_IVARS;
int _no; //4
};
打印结果
int main(int argc, const char * argv[]) {
@autoreleasepool {
Animal *animal = [[Animal alloc] init];
Dog *dog = [[Dog alloc] init];
//实际占用多少字节
NSLog(@"实际占用 animal -- %zd, dog -- %zd",class_getInstanceSize([Animal class]),class_getInstanceSize([Dog class]));
//实际分配多少字节
NSLog(@"实际分配 animal -- %zd, dog -- %zd",malloc_size((__bridge const void *)animal), malloc_size((__bridge const void *)dog));
}
return 0;
}
输出: 实际占用 animal -- 16, dog -- 16
实际分配 animal -- 16, dog -- 16
一个完整🌰🌰🌰
#import <objc/runtime.h>
#import <malloc/malloc.h>
@interface Animal : NSObject
{
int _age;
int _height;
int _weight;
}
@end
@implementation Animal
@end
@interface Dog : Animal
{
int _no;
}
@end
@implementation Dog
@end
struct NSObject_IMPL {
Class isa; //8
};
struct Animal_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age; //4
int _height; //4
int _weight; //4
};
struct Dog_IMPL {
struct Animal_IMPL Animal_IVARS;
int _no; //4
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
Animal *animal = [[Animal alloc] init];
Dog *dog = [[Dog alloc] init];
//实际占用多少字节 24
NSLog(@"实际占用 animal -- %zd, dog -- %zd",class_getInstanceSize([Animal class]),class_getInstanceSize([Dog class]));
//实际分配多少字节 32
NSLog(@"实际分配 animal -- %zd, dog -- %zd",malloc_size((__bridge const void *)animal), malloc_size((__bridge const void *)dog));
}
return 0;
}
我们分析Animal实例对象中,NSObject_IMPL占8个字节,_age占4个字节,_height占4个字节,_weight占4个字节,所以Animal对象占20个字节。
在Dog实例中Animal_IMPL占20个字节,_no占4个字节,所以Animal实例占24个字节。
但是为什么系统分配了32个字节?
内存对齐原则:
1.实际所占内存大小应该是8的倍数,例如算出实际大小是24,补齐之后应该是24;
2.系统分配给对象的内存大小最少是16个字节,并且是16的倍数,例如实际对象占用内存24,但系统会分配32;
注意:
class_getInstanceSize是获取实例对象实际所占的内存大小;程序运行时获取;8对齐
malloc_size是获取系统分配给对象的内存大小;16对齐;
sizeof运算符,获取的是类型的大小(int,size_t,结构体,指针变量等),程序编译时获取;