OC对象本质
文章目录
- OC对象本质
-
- 探索对象本质
- [objc_setProperty 源码源码](#objc_setProperty 源码源码)
- cls(isa_t中的一个字段)与类的关联原理
-
- [isa 的类型 isa_t](#isa 的类型 isa_t)
- initIsa方法
- 类&类结构的分析
-
- 类的分析
-
- 元类
- [objc_class & objc_object](#objc_class & objc_object)
-
- [objc_class与 objc_object有什么关系?](#objc_class与 objc_object有什么关系?)
- objc_object与对象的关系
- 总结
- 类结构分析
-
- 计算cache类的内存大小
- 获取bits
-
- [探索property_list(属性列表 )](#探索property_list(属性列表 ))
- 探索methods_list
探索对象本质
- 在 main 中自定义一个类 Person, 有一个属性 name
objc
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation Person
@end
- 我们可以通过终端利用 clang 将 main.m编译成 main.cpp,有以下几种编译命令,这里使用了第一种
objc
//1、将 main.m 编译成 main.cpp
clang -rewrite-objc main.m -o main.cpp
//2、将 ViewController.m 编译成 ViewController.cpp
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot / /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.7.sdk ViewController.m
//以下两种方式是通过指定架构模式的命令行,使用xcode工具 xcrun
//3、模拟器文件编译
- xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
//4、真机文件编译
- xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main- arm64.cpp
- 编译之后,我们可以看到这里的类的一个定义
objc
//NSObject的定义
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
//NSObject 的底层编译
struct NSObject_IMPL {
Class isa;
};
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
//第一个成员并不是一个新的 `isa`,而是直接把整个 `NSObject_IMPL` 结构体原样嵌入进来并命名为 `NSObject_IVARS`由于 NSObject_IMPL 内部只有一个 Class isa 成员,所以 Person_IMPL的第一个字段在内存上就是一个 Class isa
NSString *_name;
};
在这里我们可以发现 isa 的类型是 Class
- 在上一篇博客中[[[OC 底层] (一) alloc, init & new]],提到过
initInstanceIsa方法,这里面初始化 isa 指针的时候,我们是通过 isa_t类型初始化的。 - 在 NSObject 中定义的 isa 类型是 Class,这是需要做一个强制转换的,目的是让开发人员清晰明确。
总结
- OC 本质上是结构体
- Person 中的 isa 是继承自 NSObject 中的 isa
objc_setProperty 源码源码
这里面有一个 set 方法:
objc
static void _I_Person_setName_(Person * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct Person, _name), (id)name, 0, 1); }
我们可以看到里面调用了 objc_setProperty
objc
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy)
{
bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
bool mutableCopy = (shouldCopy == MUTABLE_COPY);
reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
if (offset == 0) {
object_setClass(self, newValue);
return;
}
id oldValue;
id *slot = (id*) ((char*)self + offset);
if (copy) {
newValue = [newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
} else {
if (*slot == newValue) return;
newValue = objc_retain(newValue);
}
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks.get()[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
总结
-
objc_setProperty方法的目的适用于关联 上层的set方法 以及底层的set方法,其本质就是一个接口 -
这么设计的
原因是,上层的set方法有很多,如果直接调用底层set方法中,会产生很多的临时变量,当你想查找一个sel时,会非常麻烦 -
基于上述原因,苹果采用了
适配器设计模式(即将底层接口适配为客户端需要的接口),对外提供一个接口,供上层的set方法使用,对内调用底层的set方法,使其相互不受影响

cls(isa_t中的一个字段)与类的关联原理
isa 的类型 isa_t
下面 isa 指针 isa_t的定义,下面是使用联合体定义的
objc
union isa_t {
isa_t() { } // 默认的一个构造函数
isa_t(uintptr_t value) : bits(value) { }
uintptr_t bits; // 这个和cls是一个互斥类型
private:
// Accessing the class requires custom ptrauth operations, so
// force clients to go through setClass/getClass by making this
// private.
Class cls; // 这里把cls指针私有化,禁止直接访问cls,必须通过getClass()和setClass()方法操作
public:
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h 这里是一个位域设置一个结构体
};
bool isDeallocating() {
return extra_rc == 0 && has_sidetable_rc == 0; //判断位域对应的一个标志位
}
void setDeallocating() {
extra_rc = 0;
has_sidetable_rc = 0;
}
#endif
void setClass(Class cls, objc_object *obj); //提供set方法获取cls指针更加安全
Class getClass(bool authenticated); //提供get方法获取cls指针
Class getDecodedClass(bool authenticated); 。。
}; //这里是因为C++支持里一个联合体方法,c++11以上支持联合体里面放方法,这里的方法并会被存储到我们的一个代码段,不会影响联合体的一个内存
我们看一下这里面的联合体
-
这里面提供了两个成员,
cls和bits,由联合体的定义所知,这两个成员是互斥的,也就意味着,当初始化isa指针时,有两种初始化方式- 通过
cls初始化,bits无默认值 - 通过
bits初始化,cls有默认值
- 通过
-
这里提供了一个位域的内容,用于存储类信息以及其他信息,结构体的成员
ISA_BITFIEld这里我们就看一下他是怎么定义位域的下面是源码
这是 arm 的
objc
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
这是 x86 的
objc
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
-
nonpointer有两个值,表示自定义的类等,占1位0:纯isa指针1:不只是类对象地址,isa中包含了类信息、对象的引用计数等
-
has_assoc表示关联对象标志位,占1位0:没有关联对象1:存在关联对象
-
has_cxx_dtor表示该对象是否有C++/OC的析构器(类似于dealloc),占1位- 如果
有析构函数,则需要做析构逻辑 - 如果
没有,则可以更快的释放对象
- 如果
-
shiftcls表示存储类的指针的值(类的地址), 即类信息arm64中占33位,开启指针优化的情况下,在arm64架构中有33位用来存储类指针x86_64中占44位
-
magic用于调试器判断当前对象是真的对象还是没有初始化的空间,占6位 -
weakly_refrenced是 指对象是否被指向或者曾经指向一个ARC的弱变量- 没有弱引用的对象可以更快释放
-
deallocating标志对象是是否正在释放内存 -
has_sidetable_rc表示 当对象引用计数大于10时,则需要借用该变量存储进位 -
extra_rc(额外的引用计数) ,表示该对象的引用计数值,实际上是引用计数值减1

在上一篇博客我们[[[OC 底层] (一) alloc, init & new]]我们探讨了到了 initInstanceIsa,现在我们回看一下
objc
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
ASSERT(!cls->instancesRequireRawIsa());
ASSERT(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
在这里面有一个很重要的方法
initIsa方法
这才是一个最终操纵内存的方法
objc
inline void
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
// cls: 要绑定的类
// nonpointer: true = 开启 nonpointer isa 优化;false = 纯类指针模式
// hasCxxDtor: 该类是否有 C++ 析构函数,释放时需要走析构逻辑
{
ASSERT(!isTaggedPointer()); // Tagged Pointer 不走这套流程,直接拦截
isa_t newisa(0); // 创建临时 isa,初始全零
if (!nonpointer) {
// ── raw isa 模式:8 字节全部用来存类指针,直接写入 cls ──
newisa.setClass(cls, this);
} else {
ASSERT(!DisableNonpointerIsa); // 确认 nonpointer isa 未被禁用
ASSERT(!cls->instancesRequireRawIsa()); // 确认该类不强制要求 raw isa
#if SUPPORT_INDEXED_ISA
// ── indexed isa 模式(watchOS 等特殊平台):isa 里存类数组索引而非地址 ──
ASSERT(cls->classArrayIndex() > 0); // 索引必须有效
newisa.bits = ISA_INDEX_MAGIC_VALUE; // 写入魔数,同时设置 nonpointer 和 magic 标志位
newisa.has_cxx_dtor = hasCxxDtor; // 写入析构器标志
newisa.indexcls = (uintptr_t)cls->classArrayIndex(); // 存类在全局类数组中的索引
#else
// ── 标准 nonpointer isa 模式(arm64 / x86_64 主流路径)──
newisa.bits = ISA_MAGIC_VALUE; // 整体写入魔数,同时设置 nonpointer=1 和 magic 标志位
# if ISA_HAS_CXX_DTOR_BIT
newisa.has_cxx_dtor = hasCxxDtor; // 写入析构器标志(部分平台才有独立 bit)
# endif
newisa.setClass(cls, this); // 核心:将 cls 右移 3 位编码进 shiftcls,完成对象与类的绑定
#endif
#if ISA_HAS_INLINE_RC
newisa.extra_rc = 1; // 初始化内联引用计数(extra_rc 存的是引用计数 - 1)
#endif
}
// 将构造好的 newisa 一次性写入对象的 isa 字段
// 必须单次写入:realize 类时其他线程可能同时读取 isa,分多次写会产生数据竞争
isa() = newisa;
}
这里的 initisa 的本质作用就是:把一块刚分配的裸内存,变成一个"知道自己是谁"的合法 OC 对象。
objc
newisa.setClass(cls, this);
这才是 initIsa 最核心的目的所在。setClass 把传入的 Class cls 指针右移 3 位后填入 shiftcls 位域,从这一刻起,这块内存就和某个具体的类绑定在一起了。之后 runtime 发消息时,会从对象的 isa 里取出 shiftcls,还原出类指针,再去类的方法列表里查找对应的方法------整个 OC 动态派发机制的起点,就是这里。
通过setClass方法中的shiftcls来验证绑定的一个流程
我们知道 setClass 是修改 isa 中类指针的唯一入口。
下面看一下他的相关实现
objc
inline void
isa_t::setClass(Class newCls, UNUSED_WITHOUT_PTRAUTH objc_object *obj)
{
#ifdef ISA_MASK
// Make sure the class pointer doesn't have any bits set outside the isa
// mask bits.
uintptr_t newClsBits = (uintptr_t)newCls;
if (slowpath((newClsBits & ISA_MASK) != newClsBits)) {
_objc_fatal("Invalid class pointer %p has bits set outside of ISA_MASK", newCls);
}
#endif
// Match the conditional in isa.h.
#if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
# if ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_NONE
// No signing, just use the raw pointer.
uintptr_t signedCls = (uintptr_t)newCls;
# elif ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_ONLY_SWIFT
// We're only signing Swift classes. Non-Swift classes just use
// the raw pointer
uintptr_t signedCls = (uintptr_t)newCls;
if (newCls->isSwiftStable())
signedCls = (uintptr_t)ptrauth_sign_unauthenticated((void *)newCls, ISA_SIGNING_KEY, ptrauth_blend_discriminator(obj, ISA_SIGNING_DISCRIMINATOR));
# elif ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_ALL
// We're signing everything
uintptr_t signedCls = (uintptr_t)ptrauth_sign_unauthenticated((void *)newCls, ISA_SIGNING_KEY, ptrauth_blend_discriminator(obj, ISA_SIGNING_DISCRIMINATOR));
# else
# error Unknown isa signing mode.
# endif
shiftcls_and_sig = signedCls >> 3;
#elif SUPPORT_INDEXED_ISA
// Indexed isa only uses this method to set a raw pointer class.
// Setting an indexed class is handled separately.
cls = newCls;
#else // Nonpointer isa, no ptrauth
shiftcls = (uintptr_t)newCls >> 3;
#endif
}
在这里我们做两个LLDB 相关的调试,来验证我们是否把类信息存储到 shiftcls 中
我们先看输入
po (Class)newCls ------看输入

我们输入类的地址
然后我们看输出的类的地址和输入是否一致
p/x newisa.bits & 0x00007ffffffffff8ULL ------看输出

得到的结论是一致的所以我们是把相关的类信息存储到了 shiftcls 中。
在后面还有三个方法
方法二:object_getClass
通过 runtime 提供的 API 从对象身上取出类,不需要手动做位运算,runtime 内部帮你做了 ISA_MASK 的处理
objc
po object_getClass((id)obj)
方法三:po (Class)newCls 直接打印
在 setClass 执行之前,直接打印 newCls 确认传入的类是否正确
方法四:位运算手动计算(不依赖调试器)
纯数学推导,不需要断点,直接用已知地址计算:
原始类地址:0x00000001005d90a0
右移 3 位后存入 shiftcls:0x00000001005d90a0 >> 3 = 0x000000020 0bb2140
还原:shiftcls << 3 = 0x00000001005d90a0

[Person alloc]
↓
cls = Person 的内存地址(早已存在)
↓
calloc → mem(全零裸内存)
↓
initIsa(cls=Person, this=mem)
├─ newisa(0) 全零草稿
├─ bits = MAGIC_VALUE 写标志位
├─ setClass(Person) shiftcls = Person >> 3
├─ extra_rc = 1 引用计数
└─ isa = newisa 草稿写入 mem 的头部
↓
alloc 返回 mem
↓
p = mem ← p->isa->shiftcls 里存着 Person 的地址
类&类结构的分析
类的分析
类的分析 主要是分析 isa的走向 以及 继承关系
objc
@interface Person : NSObject
{
NSString *hobby;
}
@property (nonatomic, copy) NSString *LK_Name;
- (void) sayHello;
+ (void) sayBye;
@end
@implementation Person
- (void) sayHello {
}
+ (void) sayBye {
}
@end
@interface Teacher : Person
@end
@implementation Teacher
@end
下面会有一段终端命令

如果说是理解实例->类对象->元类->根元类的话最核心的应该是这几个

这里你可以发下主要调试命令是这几个 x/4gx,p/x,po
p/x 表达式以十六进制打印
po 对象打印 OC 对象的描述
x/4gx 地址读取地址起始的 4 个 8 字节内存块
好的我们详细说一下上面的这几个操作,从第一步开始,
x/4gx person
0xae1002560: 0x00000001000083f0 0x0000000000000000
0xae1002570: 0x0000000000000000 0x0000000000000000
我们的核心目的是获得 person 的 isa 指针0x00000001000083f0,然后通过 isa 指针获得他的类也就是类对象的指针,
p/x 0x00000001000083f0 & 0x0000000ffffffff8ULL
(unsigned long long) 0x00000001000083f0
获得类指针后我么继续获得他的相关 isa 指针
x/4gx 0x00000001000083f0
0x1000083f0: 0x00000001000083c8 0x00000001fd72ed58
0x100008400: 0x00000001005c2840 0xe021000000000000
对 Person 类的 isa 做 mask,得到元类地址,
p/x 0x00000001000083c8 & 0x0000000ffffffff8ULL
(unsigned long long) 0x00000001000083c8
下来我们介绍一下什么是元类
元类
-
- 我们都知道
对象的isa是指向类,类的其实也是一个对象,可以称为类对象,其isa的位域指向苹果定义的元类
- 我们都知道
- 元类是系统给的,他的定义和创建都是由编译器玩测的,这个过程中,类的归属来自于元类
- 元类是类对象的类,每一个类都有独一无二的元类来存储类方法的相关信息
- 元类本身是没有名字的,只是因为类与之相关联,所以直接采用了类的一个名字
下面我们通过 LLDB 命令来展示出完整的一个流程

这里比上面多了一轮我么可以发现,这多的一轮就是在寻找根元类,所以最后的类的地址就是 NSObject
总结
对象的isa指向类(也可称为类对象)类的isa指向元类元类的isa指向根元类,即NSObject根元类的isa指向 它自己

isa走位
isa的走向有以下几点说明:
-
实例对象(Instance of Subclass)的isa指向类(class) -
类对象(class)isa指向元类(Meta class) -
元类(Meta class)的isa指向根元类(Root metal class) -
根元类(Root metal class)的isa指向它自己本身,形成闭环,这里的根元类就是NSObject
superclass走位
superclass(即继承关系)的走向也有以下几点说明:
-
类之间 的继承关系:-
类(subClass)继承自父类(superClass) -
父类(superClass)继承自根类(RootClass),此时的根类是指NSObject -
根类继承自nil,所以根类即NSObject可以理解为万物起源,即无中生有
-
-
元类也存在继承,元类之间的继承关系如下:-
子类的元类(metal SubClass)继承自父类的元类(metal SuperClass) -
父类的元类(metal SuperClass)继承自根元类(Root metal Class -
根元类(Root metal Class)继承于根类(Root class),此时的根类是指NSObject
-
objc_class & objc_object
isa走位我们理清楚了,又来了一个新的问题:为什么 对象 和 类都有isa属性呢?这里就不得不提到两个结构体类型:objc_class & objc_object
NSObject的底层编译是NSObject_IMPL结构体,
- 其中
Class是isa指针的类型,是由objc_class定义的类型, - 而
objc_class是一个结构体。在iOS中,所有的Class都是以objc_class为模板创建的
c
struct NSObject_IMPL {
Class isa;
};
typedef struct objc_class *Class;
下面是 objc_class 的结构体
objc
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
Class getSuperclass() const {
···代码太多了
下面是 objc_object结构体
objc
struct objc_object {
private:
char isa_storage[sizeof(isa_t)];
isa_t &isa() { return *reinterpret_cast<isa_t *>(isa_storage); }
const isa_t &isa() const { return *reinterpret_cast<const isa_t *>(isa_storage); }
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA(bool authenticated = false) const;
// rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
Class rawISA() const;
// getIsa() allows this to be a tagged pointer object
Class getIsa() const;
uintptr_t isaBits() const;
// initIsa() should be used to init the isa of new objects only.
// If this object already has an isa, use changeIsa() for correctness.
// initInstanceIsa(): objects with no custom RR/AWZ
// initClassIsa(): class objects
// initProtocolIsa(): protocol objects
// initIsa(): other objects
void initIsa(Class cls /*nonpointer=false*/);
void initClassIsa(Class cls /*nonpointer=maybe*/);
void initProtocolIsa(Class cls /*nonpointer=maybe*/);
void initInstanceIsa(Class cls, bool hasCxxDtor);
// changeIsa() should be used to change the isa of existing objects.
// If this is a new object, use initIsa() for performance.
Class changeIsa(Class newCls);
bool hasNonpointerIsa() const;
bool isTaggedPointer() const;
bool isBasicTaggedPointer() const;
bool isExtTaggedPointer() const;
bool isClass() const;
// object may have associated objects?
bool hasAssociatedObjects() const;
void setHasAssociatedObjects();
···代码太多了不展示了
objc_class与 objc_object有什么关系?
-
结构体类型
objc_class继承自objc_object类型,其中objc_object也是一个结构体,且有一个isa属性,所以objc_class也拥有了isa属性 -
mian.cpp底层编译文件中,
NSObject中的isa在底层是由Class定义的,其中class的底层编码来自objc_class类型,所以NSObject也拥有了isa属性 -
NSObject是一个类,用它初始化一个实例对象objc,objc 满足objc_object的特性(即有isa属性),主要是因为isa是由NSObject从objc_class继承过来的,而objc_class继承自objc_object,objc_object有isa属性。所以对象都有一个isa,isa表示指向,来自于当前的objc_object -
objc_object(结构体)是 当前的根对象,所有的对象都有这样一个特性objc_object,即拥有isa属性
objc_object与对象的关系
-
所有的
对象都是以objc_object为模板继承过来的 -
所有的对象 是 来自
NSObject(OC) ,但是真正到底层的 是一个objc_object(C/C++)的结构体类型
总结
-
所有的
对象+类+元类都有isa属性 -
所有的
对象都是由objc_object继承来的 -
简单概括就是
万物皆对象,万物皆来源于objc_object,有以下两点结论:-
所有以
objc_object为模板 创建的对象,都有isa属性 -
所有以
objc_class为模板,创建的类,都有isa属性
-
-
在结构层面可以通俗的理解为
上层OC与底层的对接:下层是通过结构体定义的模板,例如objc_class、objc_object上层是通过底层的模板创建的 一些类型,例如Person
objc_class、objc_object、isa、object、NSObject等的整体的关系,如下图所示

类结构分析
主要分析类信息中储存类哪些内容
探索类信息中有什么时,事先我们并不清楚类的结构是什么样的,但是我们可以通过类得到一个首地址,然后通过地址平移去获取里面所有的值
下面是 objc_class的结构体,里面有一下几个属性
objc
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;
-
isa属性:继承自objc_object的isa,占8字节 -
superclass属性:Class类型,Class是由objc_object定义的,是一个指针,占8字节 -
cache属性:简单从类型class_data_bits_t目前无法得知,而class_data_bits_t是一个结构体类型,结构体的内存大小需要根据内部的属性来确定,而结构体指针才是8字节 -
bits属性:只有首地址经过上面3个属性的内存大小总和的平移,才能获取到bits
计算cache类的内存大小
进入cache类cache_t的定义(只贴出了结构体中非static修饰的属性,主要是因为static类型的属性 不存在结构体的内存中)
objc
struct cache_t {
private:
explicit_atomic<uintptr_t> _bucketsAndMaybeMask;
union {
// Note: _flags on ARM64 needs to line up with the unused bits of
// _originalPreoptCache because we access some flags (specifically
// FAST_CACHE_HAS_DEFAULT_CORE and FAST_CACHE_HAS_DEFAULT_AWZ) on
// unrealized classes with the assumption that they will start out
// as 0.
struct {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED && !__LP64__
// Outlined cache mask storage, 32-bit, we have mask and occupied.
explicit_atomic<mask_t> _mask;
uint16_t _occupied;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED && __LP64__
// Outlined cache mask storage, 64-bit, we have mask, occupied, flags.
explicit_atomic<mask_t> _mask;
uint16_t _occupied;
uint16_t _flags;
# define CACHE_T_HAS_FLAGS 1
#elif __LP64__
// Inline cache mask storage, 64-bit, we have occupied, flags, and
// empty space to line up flags with originalPreoptCache.
//
// Note: the assembly code for objc_release_xN knows about the
// location of _flags and the
// FAST_CACHE_HAS_CUSTOM_DEALLOC_INITIATION flag within. Any changes
// must be applied there as well.
uint32_t _disguisedPreoptCacheSignature;
uint16_t _occupied;
uint16_t _flags;
cache_t 总是 16 字节,不管哪个平台。
第一个字段 _bucketsAndMaybeMask ,所有平台都是 uintptr_t,固定 8 字节,没有分歧。
第二个字段union
- 32 位 OUTLINED:
_mask(4)+_occupied(2)= 6 字节,对齐到 8 - 64 位 OUTLINED:
_mask(4)+_occupied(2)+_flags(2)= 恰好 8 字节 - 64 位内联(arm64 真机):
_disguisedPreoptCacheSignature(4)+_occupied(2)+_flags(2)= 恰好 8 字节
联合体的大小取最大成员,最大就是 8 字节,所以联合体固定 8 字节。
获取bits
bits:封装了类的其他信息,例如成员变量,方法列表,协议,属性
这里我们先来认识bits中非常重要的一个类别class_rw_t这是一个结构体类型,这里目前什么都没有需要我们进行进一步探索
objc
class_rw_t* data() const {
ASSERT(has_rw_pointer());
uintptr_t localBits = bits.load(std::memory_order_relaxed);
#if __BUILDING_OBJCDT__
return (class_rw_t *)((uintptr_t)ptrauth_strip((class_rw_t *)localBits,
CLASS_DATA_BITS_RW_SIGNING_KEY) & FAST_DATA_MASK);
#else
return (class_rw_t *)((uintptr_t)ptrauth_auth_data((class_rw_t *)localBits,
CLASS_DATA_BITS_RW_SIGNING_KEY,
ptrauth_blend_discriminator(&bits,
CLASS_DATA_BITS_RW_DISCRIMINATOR)) & FAST_DATA_MASK);
#endif
}
objc
void setData(class_rw_t *newData)
{
ASSERT(!has_rw_pointer()
|| (newData->flags & (RW_REALIZING | RW_FUTURE)));
uintptr_t authedBits;
uintptr_t localBits = bits.load(std::memory_order_relaxed);
if (objc::disableEnforceClassRXPtrAuth) {
authedBits = (uintptr_t)ptrauth_strip((const void *)localBits,
CLASS_DATA_BITS_RW_SIGNING_KEY);
} else {
if (localBits == 0) {
authedBits = 0;
} else if (has_rw_pointer(localBits)) {
authedBits = (uintptr_t)ptrauth_auth_data((class_rw_t *)localBits,
CLASS_DATA_BITS_RW_SIGNING_KEY,
ptrauth_blend_discriminator(&bits,
CLASS_DATA_BITS_RW_DISCRIMINATOR));
} else {
authedBits = (uintptr_t)ptrauth_auth_data((class_ro_t *)localBits,
CLASS_DATA_BITS_RO_SIGNING_KEY,
ptrauth_blend_discriminator(&bits,
CLASS_DATA_BITS_RO_DISCRIMINATOR));
}
}
// Set during realization or construction only. No locking needed.
// Use a store-release fence because there may be concurrent
// readers of data and data's contents.
uintptr_t newBits = ((authedBits & FAST_FLAGS_MASK)
| (uintptr_t)newData
| FAST_IS_RW_POINTER);
class_rw_t *signedData
= ptrauth_sign_unauthenticated((class_rw_t *)newBits,
CLASS_DATA_BITS_RW_SIGNING_KEY,
ptrauth_blend_discriminator(&bits,
CLASS_DATA_BITS_RW_DISCRIMINATOR));
bits.store((uintptr_t)signedData, std::memory_order_release);
}
objc
const class_ro_t *safe_ro() const {
// Load bits once, then work on that value. This prevents us from seeing
// !has_rw_pointer, then concurrently a class_rw_t pointer is stored,
// then we try to authenticate it using the RO signing scheme, or return
// a class_rw_t* as a class_ro_t* when ptrauth is not enabled.
uintptr_t bitsValue = bits.load(std::memory_order_relaxed);
if (has_rw_pointer(bitsValue)) {
return data()->ro();
}
uintptr_t authedBits;
if (authentication == Authentication::Strip || objc::disableEnforceClassRXPtrAuth) {
authedBits = (uintptr_t)ptrauth_strip((const void *)bitsValue,
CLASS_DATA_BITS_RO_SIGNING_KEY);
} else {
authedBits = (uintptr_t)ptrauth_auth_data((const void *)bitsValue,
CLASS_DATA_BITS_RO_SIGNING_KEY,
ptrauth_blend_discriminator(&bits,
CLASS_DATA_BITS_RO_DISCRIMINATOR));
}
return (const class_ro_t *)(authedBits & FAST_DATA_MASK);
}
在这里有 bits 中最重要的两个方法,一个是safe_ro它返回的是 class_ro_t 另一个是data返回的是 class_rw_t。
探索property_list(属性列表 )
通过查看class_rw_t这个类.我们可以发现这里的一个属性列表和方法列表等多个列表

这里的存储在 class_rw_t这个类是储存一个属性的,不储存成员变量和实例变量
成员变量存在 class_ro_t
objc
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
union {
const uint8_t * ivarLayout;
Class nonMetaclass;
};
explicit_atomic<const char *> name;
objc::PointerUnion<method_list_t, relative_list_list_t<method_list_t>, method_list_t::Ptrauth, method_list_t::Ptrauth> baseMethods;
objc::PointerUnion<protocol_list_t, relative_list_list_t<protocol_list_t>, PtrauthRaw, PtrauthRaw> baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
objc::PointerUnion<property_list_t, relative_list_list_t<property_list_t>, PtrauthRaw, PtrauthRaw> baseProperties;
// This field exists only when RO_HAS_SWIFT_INITIALIZER is set.
_objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0];
_objc_swiftMetadataInitializer swiftMetadataInitializer() const {
if (flags & RO_HAS_SWIFT_INITIALIZER) {
return _swiftMetadataInitializer_NEVER_USE[0];
} else {
return nil;
}
}
const char *getName() const {
return name.load(std::memory_order_acquire);
}
class_ro_t *duplicate() const {
bool hasSwiftInitializer = flags & RO_HAS_SWIFT_INITIALIZER;
size_t size = sizeof(*this);
if (hasSwiftInitializer)
size += sizeof(_swiftMetadataInitializer_NEVER_USE[0]);
class_ro_t *ro = (class_ro_t *)memdup(this, size);
if (hasSwiftInitializer)
ro->_swiftMetadataInitializer_NEVER_USE[0] = this->_swiftMetadataInitializer_NEVER_USE[0];
#if __has_feature(ptrauth_calls)
// Re-sign the method list pointer.
ro->baseMethods = baseMethods;
#endif
return ro;
}
Class getNonMetaclass() const {
ASSERT(flags & RO_META);
return nonMetaclass;
}
const uint8_t *getIvarLayout() const {
if (flags & RO_META)
return nullptr;
return ivarLayout;
}
};
这里面有方法,属性,协议还有成员变量,但方法,属性,协议的开头是用base开头的。
我们想要获得成员变量列表,这个时候需要用到ivars这个列表包含的内容不仅仅包含一个成员变量列表,除了包括在{}中定义的一个成员变量,还包括通过属性定义的成员变量.bits --> data() -->ro() --> ivars通过这个流程来获取成员变量表。
its --> data() --> ro() --> ivars 这条访问链说明,成员变量列表 ivars 存储在 class_ro_t 里,必须经过 bits → class_rw_t(通过 data())→ class_ro_t(通过 ro())才能拿到。
通过@property定义的属性,也会存储在bits属性中,通过bits --> data() --> properties() --> list获取属性列表,其中只包含属性。
探索methods_list
这里的 methods_list是不储存方法的,他只储存实例方法,存在类中,
Person类 --> bits --> methods() --> list
我们的类方法存储在元类的 bits里,
Person元类 --> bits --> methods() --> list