只是小小记录一下,内容不完整
- OC底层实现是c\c++,oc的面向对象都是基于c\c++实现
- NSObject c++ 底层结构
struct NSObject_IMPL {
//CLass 指针,64位占8个字节
__unsafe_unretained Class isa;
};
//Class
typedef struct objc_class *Class
oc中的定义
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
//
typedef struct objc_class *Class;
- 一个NSObject对象占用多少内存?
- 系统分配了16个字节给nsobject对象(通过malloc_size函数获得)
通过查看alloc源码,可以看到为NSObject对象分配内存时,硬性规定至少为16字节 - 但NSObject对象实际只使用8个字节(64位环境下,通过class_getInstanceSize获得,其实际调用的函数如下,从注释中可知这是成员变量的大小,返回的是内存对齐后的大小)
返回内存对齐
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
@interface Student : NSObject
@property (nonatomic, assign) char num;
@property (nonatomic, assign) int age;
@end
@implementation Student
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// // insert code here...
Student *stu = [[Student alloc] init];
stu.age = 10;
stu.num = 'a';
//class_getInstanceSize返回的是类的对象的成员变量所占内存大小(对齐过的)
NSLog(@"%lu", class_getInstanceSize([Student class]));
//返回stu指针所指向的内存大小
NSLog(@"%lu", malloc_size((__bridge const void *)stu));
}
return 0;
}
//打印结果
//2021-07-21 16:19:36.975209+0800 对象的本质[27424:2825069] 16
//2021-07-21 16:19:36.977476+0800 对象的本质[27424:2825069] 16
//转为c++, Student本质
struct Student_IMPL {
struct NSObject_IMPL NSObject_IVARS; //只有isa, 8个字节
char _num; //1
int _age; //4
};
//NSOject本质
struct NSObject_IMPL {
__unsafe_unretained Class isa;
};
分别存储着num,age(16进制, iOS是小端模式,内存读取要反着读)
!!!内存对齐 结构体嵌套 学习!!!!!
- 结构体内存对齐原则:
- 第一个成员从偏移量为0开始存储,其余成员从地址为自身大小的整数倍开始存储。
- 成员为结构体时,该结构体从地址为其最大成员的整数倍开始存储
- 结构体的总大小,即sizeof()出来的结果是其最大成员大小的整数倍。
- 补充
- 获取内存大小
- sizeof:操作符:
传入数据类型,输出内存大小。编译时固定,只与类型相关,与具体数值无关。(如:bool 2字节,int 4字节,对象(指针)8字节) - class_getInstanceSize: runtime的api,传入对象,输出对象实际所占的内存大小,本质是对象中成员变量的大小。与sizeof()返回值相同。
- malloc_size:
获取系统实际分配的内存大小
有一个对齐的概念,与内存对齐概念不同,为oc对象分配的内存大小为16字节倍数,空间换时间,以16字节为一块,这就保证了CPU在读取的时候,按照块读取就可以,效率更高,同时还不容易混乱。()
- oc对象的分类
- instance对象,实例对象
在内存中存储的信息 有isa、成员变量的具体值 - class对象,类对象
类对象在内存只有一份
类对象存储的内容为
isa指针、superclass指针
类的属性信息、类的对象方法信息
类的协议信息、类的成员变量信息
…
- meta-class对象,元类对象
object_getClass获取元类对象,参数为类对象
注意:class方法返回的对象一定是类对象,不是元类对象
每个类只有一个元类对象
元类对象存储的内容:
isa、superclass指针
类方法
…
- objc_getClass(<const char * _Nonnull name>)
参数为字符串类名,返回相应的类对象
object_getClass(id _Nullable obj)
参数为实例对象,返回类对象
参数为类对象,返回元类对象
参数为元类对象,返回NSObject(基类)
- isa指针
实例对象isa指向类对象
类对象isa指向元类对象
元类对象isa指向基类的元类对象
实际上,在64bit开始,isa需要进行一次位运算即 isa & ISA_MASK,才能计算出指向的类或元类对象的真实地址
对于ISA_MASK的定义
- struct objc_class的结构
//objc-runtime-new.h中
//继承的objc_object下面有复制部分 里面的成员只有isa
//
struct objc_class : objc_object {
objc_class(const objc_class&) = delete;
objc_class(objc_class&&) = delete;
void operator=(const objc_class&) = delete;
void operator=(objc_class&&) = delete;
// Class ISA;
Class superclass;
cache_t cache; // 方法缓存 formerly cache pointer and vtable
class_data_bits_t bits; // 用于获取具体的类信息 class_rw_t * plus custom rr/alloc flags
...
}
struct objc_object {
private:
isa_t isa;
public:
...
//一些方法
}
struct objc_class的结构可以简化为
struct objc_class {
// Class ISA;
Class superclass;
cache_t cache; // 方法缓存 formerly cache pointer and vtable
class_data_bits_t bits; // 用于获取具体的类信息 class_rw_t * plus custom rr/alloc flags
}
- 对class_rw_t的理解
rw代表可读可写
类中的属性、方法、协议等信息都保存在class_rw_t中 - 对class_ro_t的理解
ro代表只读
存储了当前类在编译期就确定了的属性、方法、协议
OC对象中存储的属性、方法、遵循的协议数据其实被存储在这两块儿内存区域的,而我们通过runtime动态修改类的方法时,是修改在class_rw_t区域中存储的方法列表