Objective-C属于一种基于c/c++而封装的面向对象的高级语言,其编译过程如下:
即:Objective-C的面向对象都是基于C/C++的数据结构实现的,而Objective-C的对象、类主要是基于C/C++的结构体实现的,本质上Objective-C的对象、类就是结构体
为什么OC对象的本质是结构体
NSObject
首先我们先创建一个程序
在main.m文件中创建一个NSObject的对象
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
}
return 0;
}
使用终端将main.m编译成.cpp文件,命令如下:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
*打开main.cpp文件,搜索NSObject,可找到如下代码:
istruct NSObject_IMPL {
Class isa;
};
即:创建的obj就是一个结构体,这个结构体就是NSObject对象在内存中的本质。
另外,我们按住command点击进NSObject里面看一下,也可以看到这样一个结构:
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
然后我们按住command键点击Class进入窥探一下这个Class,我们看到这样一个结构:
typedef struct objc_class *Class;
这说明Class是一个结构体指针。所以isa也就是一个指针。因此NSObject_IMPL这个结构体中就是包含了一个结构体指针isa,它所占的内存大小就是这个isa指针所占的内存大小。
在64位环境中,指针占8个字节,在32位环境中,指针占4个字节。
NSObject_IMPL这个结构体只有一个成员isa指针,所以结构体的地址就是存放isa指针的地址。比如isa这个指针的地址是0x100400100,那么就有objc=0x100400110。
自定义的类
在main.m文件中创建一个继承自NSObject的Person对象
@interface Student: NSObject {
@public
int _age;
int _score;
}
@end
@implementation Student
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *stu = [[Student alloc] init];
stu->_age = 4;
stu->_score=80;
}
return 0;
}
同上的方式生成main.cpp文件,搜索Student,可看到如下内容:
struct Student_IMPL {
struct NSObject_IMPL NSObject_IVARS; /// 相当于 Class isa;
int _age;
int _score;
};
/// 由上得到,即一个初创的Student对象所占的内存大小为:isa的大小 + int的大小*2
struct Student_IMPL {
Class isa;
int _age;
int _score;
};
假设Student对象是一个结构体的话
/// 定义这样的结构体
struct Student_IMPL {
Class isa;
int _age;
int _score;
};
@interface Student: NSObject {
@public
int _age;
int _score;
}
@end
@implementation Student
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *stu = [[Student alloc] init];
stu->_age = 4;
stu->_score=80;
struct Student_IMPL *stu2 = (__bridge struct Student_IMPL *)stu;
/// 如果打印结果是 4 - 80 的话,即可证明student对象本质上就是一个结构体
NSLog(@"%d - %d", stu2->_age, stu2->_score);
}
return 0;
}
由打印结果反推得出:student对象本质上就是一个结构体
扩展:NSObject所占内存大小是多少?
因为OC对象的本质是结构体,那NSObject所占的内存大小就是结构体指针所占的大小。
其实不然,准确的说法是:(以64bit为例)
1、从内存中动态分配的大小是由malloc_size()函数获取的,即16
2、NSObject对象真正使用的大小是 8
所以一个NSObject对象在64位环境中占8字节,在32位环境中占4字节。我们接着往下看,通过读取内存来验证我们的想法。
- class_getInstanceSize()方法 (可以通过运行时中的class_getInstanceSize:方法直接获取NSObject的内存大小)
- class_getInstanceSize()返回的NSObject_IMPL的大小。
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
// 16个字节
// 获得NSObject实例对象的成员变量所占用的大小 >> 8
NSLog(@"%zd", class_getInstanceSize([NSObject class]));
// 获得obj指针所指向内存的大小 >> 16
NSLog(@"%zd", malloc_size((__bridge const void *)obj));
// 什么平台的代码
// 不同平台支持的代码肯定是不一样
// Windows、mac、iOS
// 模拟器(i386)、32bit(armv7)、64bit(arm64)
}
return 0;
}
运行上面的代码我们可以看到NSObject所占的内存大小