**NSObject**的底层实现
@interface NSObject {
Class isa
}
@end
struct NSObject_IMPL {
Class isa;
};
typedef struct objc_class *Class;
问题:一个NSObject对象占用多少内存?
NSObject *object = [[NSObject alloc] init];
// NSObject对象本质是一个结构体,结构体只有一个isa成员变量,它是一个指针。
// 这个指针在64位系统上占用8个字节
#import <objc/runtime.h>
#import <malloc/malloc.h>
// NSObject实例对象的内存大小 8
NSLog(@"%zd", class_getInstanceSize([NSObject class]));
// obj指针所指向的内存大小 16 (实际分配大小)
NSLog(@"%zd", malloc_size((__bridge const void *)(obj)));
OC对象的分类
- 实例对象(
instance) - 类对象(
class) - 元类对象(
meta-class)
实例对象
- 实例对象中存储:成员变量(
isa指针)
类对象 (每个类只有一个类对象)
类对象中存储信息:
① isa指针
② superClass指针
③ 类的属性(@property)、类的对象方法(instance method)
④ 类的协议(protocol)、类的成员变量(ivar)
⑤ 类的其他描述信息
NSObject *obj = [[NSObject alloc] init];
Class objClass1 = [obj class];
Class objClass2 = object_getClass(obj);
Class objClass3 = [NSObject class];
Class objClass4 = [objClass1 class];
objClass1、objClass2和objClass3是同一个类对象
类对象调用class方法,返回的是它本身
元类对象(每个类只有一个元类对象)
元类对象和类对象的内存结构是一样的,都是Class(object_class结构体),主要包括:
① isa指针
② superClass指针
③ 类方法
④ 其他信息
NSObject *obj = [[NSObject alloc] init];
//将类对象传入,得到元类对象
Class metaClass1 = object_getClass([NSObject class]);
Class metaClass2 = object_getClass([obj class]);
Class metaClass3 = object_getClass(object_getClass(obj));
//判断是否是元类对象
class_isMetaClass(metaClass1);
总结:
-
object_getClass方法返回的是对象的isa指针所指向的对象 -
class方法返回的是对象本身 -
从
64bit开始,实例对象的isa需要进行按位与运算(&ISA_MASK)才能得到类对象的地址 -
实例对象的
superClass指针直接指向类对象地址
为什么isa指针需要进行按位与才能获取类对象地址呢?
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
# if __arm64__# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
// 0代表普通的指针,存储着Class,Meta-Class对象的内存地址。
// 1代表优化后的使用位域存储更多的信息。
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1; // 是否有设置过关联对象,如果没有,释放时会更快
uintptr_t has_cxx_dtor : 1; // 是否有C++析构函数,如果没有,释放时会更快
// 存储着Class、Meta-Class对象的内存地址信息
uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
uintptr_t magic : 6; // 用于在调试时分辨对象是否未完成初始化
uintptr_t weakly_referenced : 1; // 是否有被弱引用指向过。
uintptr_t deallocating : 1; // 对象是否正在释放
// 引用计数器是否过大无法存储在isa中
// 如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19; // 里面存储的值是引用计数器减1#
define RC_ONE (1ULL<<45)#
define RC_HALF (1ULL<<18) };
#endif
};
isa是一个指针,占8个byte,总共64bit,OC采用联合体定义构建isa,并使用位域存储着不同的配置信息,其中类对象地址只占用33位,isa指针通过跟掩码进行按位与计算后,就能拿到对应的类对象地址。
ISA_MASK转为二进制之后为:111111111111111111111111111111111000
知识点:
① 数据类型占内存的位数
数据类型 32位 64位
char 1 1
short int 2 2
int 4 4
long int 4 8
long long int 8 8
char* 4 8
float 4 4
double 8 8
1个字节(Byte)等于8位(bit)二进制位
② iOS平台默认内存对齐系数为8
iOS堆内存分配时,都是以16的倍数进行分配
③ 将OC代码转为C++代码
clang -rewrite-objc main.m -o main.cpp
将OC代码转为指定cpu架构的C++代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
搜索objc4下载运行时源码