2019-10-10
在Runtime源代码解读(实现面向对象初探)中,从Cocoa框架中的runtime.h
头文件公开的数据结构及 API 对 runtime 整体有一个大概的认知。从本文开始具体分析 Apple 开源的runtime源代码。本文介绍 runtime 如何通过C语言结构体实现类和对象,该部分应该是 runtime 的最核心代码。
注意:Github 搜索到的有一千多颗星的RetVal/objc-runtime工程,版本是750,最新公开的代码版本是756,后者在 ARC 支持、
ivarLayout
定义、Swift 兼容等方面有变动。
一、类的定义
Objective-C 中类的本质是objc_class
结构体,其定义代码如下,包含以下成员:
isa
:objc_class
继承objc_object
结构体,因此也包含isa
指针,主要功能是指向对象的类型,新版本 runtime 中,isa
指针并不一定是Class
类型而是包含64 bit 数据的位图(bitmap),在 4.1 中详细介绍;superclass
:指向父类的指针,用于组织类的继承链;cache
:类使用哈希表数据结构缓存最近调用方法,以提高方法查找效率(TODO:后续独立文章中会介绍);bits
:class_data_bits_t
结构体类型,该结构体主要用于记录,保存类的数据的class_rw_t
结构体的内存地址。通过date()
方法访问bits
的有效位域指向的内存空间,返回class_rw_t
结构体;setData(class_rw_t *newData)
用于设置bits
的值;
注意:上述 bitmap 并不是图片的位图,而是指数据被视为简单的二进制数,将其中的一些或所有 bit 赋予特殊的含义,共同表示一种含义的 bit 或 bit 的集合称为位域。
struct objc_class : objc_object {
// 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 *data() {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
// 类的方法在文章第三部分详细介绍
...
};
二、类的数据
类的数据主要保存在class_data_bits_t
结构体中,其成员仅有一个bits
指针。objc_class
的data()
方法用于获取bits
成员的 4~47 位域(FAST_DATA_MASK
)中保存的class_rw_t
结构体地址。类的数据保存在class_rw_t
结构体中,剩余的部分保存在ro
指针指向的class_ro_t
结构体中。
class_rw_t
、class_ro_t
结构体名中,rw
是 read write 的缩写,ro
是 read only 的缩写,可见class_ro_t
的保存类的只读信息,这些信息在类完成注册后不可改变。以类的成员变量列表为例(成员变量列表保存在class_ro_t
结构体中)。若应用类注册到内存后,使用类构建了若干实例,此时若添加成员变量必然需要对内存中的这些类重新分配内存,这个操作的花销是相当大的。若考虑再极端一些,为根类NSObject
添加成员变量,则内存中基本所有 Objective-C 对象都需要重新分配内存,如此庞大的计算量在运行时是不可接受的。
#if !__LP64__
#define FAST_DATA_MASK 0xfffffffcUL
#elif 1
#define FAST_DATA_MASK 0x00007ffffffffff8UL
#endif
#if (!__LP64__ || TARGET_OS_WIN32 || \
(TARGET_OS_SIMULATOR && !TARGET_OS_IOSMAC))
# define SUPPORT_PACKED_ISA 0
#else
# define SUPPORT_PACKED_ISA 1
#endif
#if !SUPPORT_INDEXED_ISA && !SUPPORT_PACKED_ISA
# define SUPPORT_NONPOINTER_ISA 0
#else
# define SUPPORT_NONPOINTER_ISA 1
#endif
struct class_data_bits_t {
uintptr_t bits;
private:
bool getBit(uintptr_t bit)
{
return bits & bit;
}
...
public:
// 获取类的数据
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
// 设置类的数据
void setData(class_rw_t *newData)
{
// 仅在类注册、构建阶段才允许调用setData
assert(!data() || (newData->flags & (RW_REALIZING | RW_FUTURE)));
uintptr_t newBits = (bits & ~FAST_DATA_MASK) | (uintptr_t)newData;
atomic_thread_fence(memory_order_release);
bits = newBits;
}
...
// 是否支持非指针类型isa,在4.1介绍对象的isa指针时详细介绍
#if FAST_REQUIRES_RAW_ISA
bool instancesRequireRawIsa() {
return getBit(FAST_REQUIRES_RAW_ISA);
}
void setInstancesRequireRawIsa() {
setBits(FAST_REQUIRES_RAW_ISA);
}
#elif SUPPORT_NONPOINTER_ISA
// 主流机型一般走到这个编译分支
bool instancesRequireRawIsa() {
return data()->flags & RW_REQUIRES_RAW_ISA;
}
void setInstancesRequireRawIsa() {
data()->setFlags(RW_REQUIRES_RAW_ISA);
}
#else
bool instancesRequireRawIsa() {
return true;
}
void setInstancesRequireRawIsa() {
// nothing
}
#endif
...
};
1.1 class_rw_t 结构体
类的主要数据保存在bits
中,bits
以位图保存class_rw_t
结构体,用于记录类的关键数据,如成员变量列表、方法列表、属性列表、协议列表等等,class_rw_t
仅包含三个基本的位操作方法。class_rw_t
包含以下成员:
flags
:32位位图,标记类的状态;version
:标记类的类型,0
表示类为非元类,7
表示类为元类;ro
:保存类的只读数据,注册类后ro
中的数据标记为只读,成员变量列表保存在ro
中;methods
:方法列表,其类型method_array_t
为二维数组容器(TODO:后续在独立文章介绍);properties
:属性列表,其类型property_array_t
为二维数组容器(TODO:后续在独立文章介绍);protocols
:协议列表,其类型protocol_array_t
为二维数组容器;firstSubclass
:类的首个子类,与nextSiblingClass
记录所有类的继承链组织成的继承树;nextSiblingClass
:类的下一个兄弟类;demangledName
:类名,来自Swift的类会包含一些特别前缀,demangledName
是处理后的类名;index
:标记类的对象的isa
是否为index
类型;
#if __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__)
# define SUPPORT_INDEXED_ISA 1
#else
# define SUPPORT_INDEXED_ISA 0
#endif
struct class_rw_t {
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
#if SUPPORT_INDEXED_ISA
uint32_t index;
#endif
//设置set指定的位
void setFlags(uint32_t set)
{
OSAtomicOr32Barrier(set, &flags);
}
// 清空clear指定的位
void clearFlags(uint32_t clear)
{
OSAtomicXor32Barrier(clear, &flags);
}
// 设置set指定的位,清空clear指定的位
void changeFlags(uint32_t set, uint32_t clear)
{
assert((set & clear) == 0);
uint32_t oldf, newf;
do {
oldf = flags;
newf = (oldf | set) & ~clear;
} while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
}
};
class_rw_t
的flags
成员中比较重要的一些位域定义列举如下,均以RW_
为前缀,这些位域在类注册后仍可读写。
/************* 类注册后可读写的flags位域 *************/
// 类是已经注册的类
#define RW_REALIZED (1<<31)
// 类是尚未解析的future class
#define RW_FUTURE (1<<30)
// 类是已经初始化的类
#define RW_INITIALIZED (1<<29)
// 类是正在初始化的类
#define RW_INITIALIZING (1<<28)
// class_rw_t->ro是class_ro_t的堆拷贝
// 此时类的class_rw_t->ro是可写入的,拷贝之前ro的内存区域锁死不可写入
#define RW_COPIED_RO (1<<27)
// 类是正在构建而仍未注册的类
#define RW_CONSTRUCTING (1<<26)
// 类是已经构建完成并注册的类
#define RW_CONSTRUCTED (1<<25)
// 类是load方法已经调用过的类
#define RW_LOADED (1<<23)
#if !SUPPORT_NONPOINTER_ISA
// 类是可能实例可能存在关联对象的类
// 默认编译选项下,无需定义该位,因为都可能有关联对象
#define RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS (1<<22)
#endif
// 类是具有实例相关的GC layout的类
#define RW_HAS_INSTANCE_SPECIFIC_LAYOUT (1 << 21)
// 类是禁止使用关联对象的类
#define RW_FORBIDS_ASSOCIATED_OBJECTS (1<<20)
// 类是正在注册,但是未注册完成的类
#define RW_REALIZING (1<<19)
1.1 class_ro_t 结构体
类完成注册后,类的实例占用的内存大小、成员变量列表、成员变量内存布局等重要信息需要固定下来,这些在类注册后需要标记为只读的数据保存在class_ro_t
结构体中,class_rw_t
结构体的ro
成员为指向该结构体的指针。class_ro_t
结构体包含以下主要成员:
flags
:32位位图,标记类的状态。需要注意class_ro_t
的flags
使用的位域和前面介绍的class_rw_t
的flags
使用的位域是完全不同的;instanceStart
:类的成员变量,在实例的内存空间中的起始偏移量;instanceSize
:类的实例占用的内存空间大小;ivarLayout
:成员变量内存布局,标记实例占用的内存空间中哪些WORD保存了成员变量数据;name
:类名;baseMethodList
:基础方法列表,在类定义时指定的方法列表;baseProtocols
:协议列表;ivars
:成员变量列表;weakIvarLayout
:weak成员变量布局;baseProperties
:基础属性列表,在类定义时指定的属性列表;
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
...
method_list_t *baseMethods() const {
return baseMethodList;
}
class_ro_t *duplicate() const {
if (flags & RO_HAS_SWIFT_INITIALIZER) {
size_t size = sizeof(*this) + sizeof(_swiftMetadataInitializer_NEVER_USE[0]);
class_ro_t *ro = (class_ro_t *)memdup(this, size);
ro->_swiftMetadataInitializer_NEVER_USE[0] = this->_swiftMetadataInitializer_NEVER_USE[0];
return ro;
} else {
size_t size = sizeof(*this);
class_ro_t *ro = (class_ro_t *)memdup(this, size);
return ro;
}
}
};
class_ro_t
的flags
成员中比较重要的一些位域定义列举如下,均以RO_
为前缀,这些位域在类注册后标记为只读。
/************* 类注册后只读的flags位域 *************/
// 类是元类
#define RO_META (1<<0)
// 类是根类
#define RO_ROOT (1<<1)
// 类有CXX构造/析构函数
#define RO_HAS_CXX_STRUCTORS (1<<2)
// 类有实现load方法
// #define RO_HAS_LOAD_METHOD (1<<3)
// 隐藏类
#define RO_HIDDEN (1<<4)
// class has attribute(objc_exception): OBJC_EHTYPE_$_ThisClass is non-weak
#define RO_EXCEPTION (1<<5)
// class has ro field for Swift metadata initializer callback
#define RO_HAS_SWIFT_INITIALIZER (1<<6)
// 类使用ARC选项编译
#define RO_IS_ARC (1<<7)
// 类有CXX析构函数,但没有CXX构造函数
#define RO_HAS_CXX_DTOR_ONLY (1<<8)
// class is not ARC but has ARC-style weak ivar layout
#define RO_HAS_WEAK_WITHOUT_ARC (1<<9)
// 类禁止使用关联对象
#define RO_FORBIDS_ASSOCIATED_OBJECTS (1<<10)
// class is in an unloadable bundle - must never be set by compiler
#define RO_FROM_BUNDLE (1<<29)
// class is unrealized future class - must never be set by compiler
#define RO_FUTURE (1<<30)
// class is realized - must never be set by compiler
#define RO_REALIZED (1<<31)
注意:实际上在类的构建阶段,有时会操作
class_rw_t
去置flags
中一些RO_
前缀的位域,但仅发生在重叠的29/30/31位域。
三、类的行为
本章介绍objc_class
结构体中定义的方法。
3.1 类加载过程相关行为
调用 runtime API 动态创建类的过程,包括三个步骤:
- 调用
Class objc_allocateClassPair(...)
构建类; - 添加必要的成员变量、方法等元素;
- 调用
void objc_registerClassPair(Class cls)
注册类;
然而,runtime 从镜像(image)加载类的过程会更加精细,在加载类的不同阶段会被标记为不同的类型(还是objc_class
结构体,只是flags
不同),例如:future class(懒加载类)、remapped class(已重映射类)、realized class(已认识类)、allocated class(已分配内存类)、named class(已确定名称类)、loaded class(已加载类)、initialized class(已初始化类)等。接下来重点介绍 future class、remapped class 和 realized class。其中标记为 allocated class 和 named class 只是简单地将类添加到全局管理的哈希表中,因此仅穿插在 future class、remapped class 中介绍;loaded class、initialized class 分别为已执行load
方法的类和已执行initialize()
方法的类。
objc_class
结构体中与类的加载过程相关的函数代码如下,基本在class_rw_t
、class_ro_t
的flags
中存在RW_
、RO_
前缀的位域与之对应:
// 查询是否正在初始化(initializing)
bool isInitializing() {
return getMeta()->data()->flags & RW_INITIALIZING;
}
// 标记为正在初始化(initializing)
void setInitializing() {
assert(!isMetaClass());
ISA()->setInfo(RW_INITIALIZING);
}
// 是否已完成初始化(initializing)
bool isInitialized() {
return getMeta()->data()->flags & RW_INITIALIZED;
}
void setInitialized(){
Class metacls;
Class cls;
assert(!isMetaClass());
cls = (Class)this;
metacls = cls->ISA();
// 关于alloc/dealloc/Retain/Release等特殊方法的判断及处理
...
metacls->changeInfo(RW_INITIALIZED, RW_INITIALIZING);
}
bool isLoadable() {
assert(isRealized());
return true; // any class registered for +load is definitely loadable
}
// 获取load方法的IMP
IMP
objc_class::getLoadMethod()
{
runtimeLock.assertLocked();
const method_list_t *mlist;
assert(isRealized());
assert(ISA()->isRealized());
assert(!isMetaClass());
assert(ISA()->isMetaClass());
// 在类的基础方法列表中查询load方法的IMP
mlist = ISA()->data()->ro->baseMethods();
if (mlist) {
for (const auto& meth : *mlist) {
const char *name = sel_cname(meth.name);
if (0 == strcmp(name, "load")) {
return meth.imp;
}
}
}
return nil;
}
// runtime是否已认识类
bool isRealized() {
return data()->flags & RW_REALIZED;
}
// 是否future class
bool isFuture() {
return data()->flags & RW_FUTURE;
}
3.1.1 future class
objc_class
的isFuture()
函数,用于判断类是否为 future class。本节通过代码一步步探讨 future class 的概念,future class 对理解类的加载过程有重要作用。
3.1.1.1 future class 生成
首先看 future class 是如何生成的。addFutureNamedClass(const char *name, Class cls)
函数用于将传入的cls
参数,配置为类名为name
的 future class,包含以下操作:
- 分配
cls
所需的class_rw_t
、class_ro_t
的内存空间; - 将
cls
的类名置为name
; - 将
class_rw_t
的RO_FUTURE
位置为1,RO_FUTURE
等于RW_FUTURE
; - 以
name
为关键字,将cls
添加到一个全局的哈希表futureNamedClasses
;
static NXMapTable *future_named_class_map = nil;
static NXMapTable *futureNamedClasses()
{
runtimeLock.assertLocked();
if (future_named_class_map) return future_named_class_map;
// future_named_class_map is big enough for CF’s classes and a few others
future_named_class_map =
NXCreateMapTable(NXStrValueMapPrototype, 32);
return future_named_class_map;
}
static void addFutureNamedClass(const char *name, Class cls)
{
void *old;
class_rw_t *rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
class_ro_t *ro = (class_ro_t *)calloc(sizeof(class_ro_t), 1);
ro->name = strdupIfMutable(name);
rw->ro = ro;
cls->setData(rw);
cls->data()->flags = RO_FUTURE;
old = NXMapKeyCopyingInsert(futureNamedClasses(), name, cls);
assert(!old);
}
追踪调用addFutureClass(...)
的代码,最终追溯到Class objc_getFutureClass(const char *name)
,该函数并没有在 runtime 源代码中被调用到。而用于从namedFutureClasses
哈希表中获取 future class 的popFutureClass(...)
函数是有间接通过readClass(...)
函数被广泛调用。因此,构建 future class 的逻辑大多隐藏在 runtime 的内部实现中未公布,只有使用 future class 的逻辑是开源的。
Class objc_getFutureClass(const char *name)
{
Class cls;
cls = look_up_class(name, YES, NO);
if (cls) {
if (PrintFuture) {
_objc_inform("FUTURE: found %p already in use for %s",
(void*)cls, name);
}
return cls;
}
// 若查找不到名为name的类则构建future class
return _objc_allocateFutureClass(name);
}
Class _objc_allocateFutureClass(const char *name)
{
mutex_locker_t lock(runtimeLock);
Class cls;
NXMapTable *map = futureNamedClasses();
if ((cls = (Class)NXMapGet(map, name))) {
// 存在名为name的 future class
return cls;
}
// 分配用于保存objc_class的内存空间
cls = _calloc_class(sizeof(objc_class));
// 构建名为name的future class并全局记录到 futureNamedClasses 哈希表
addFutureNamedClass(name, cls);
return cls;
}
3.1.1.2 future class 应用
addFutureClass(...)
操作明显是全局记录 future class 的过程,接下来追溯 何时用到 future class。static Class popFutureNamedClass(const char *name)
用于从futureNamedClasses
哈希表中弹出类名为name
的 future class,这是获取全局记录的 future class 的唯一入口。
static Class popFutureNamedClass(const char *name)
{
runtimeLock.assertLocked();
Class cls = nil;
if (future_named_class_map) {
cls = (Class)NXMapKeyFreeingRemove(future_named_class_map, name);
if (cls && NXCountMapTable(future_named_class_map) == 0) {
NXFreeMapTable(future_named_class_map);
future_named_class_map = nil;
}
}
return cls;
}
popFutureNamedClass
在Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
函数中有被调用到,后者用于读取cls
中的类数据,关键处理逻辑表述如下:
- 若
futureNamedClasses
哈希表中存在cls->mangledName()
类名的 future class,则将cls
重映射(remapping)到新的类newCls
(具体重映射过程在 3.1.2 中详细讨论),然后将newCls
标记为 remapped class,以cls
为关键字添加到全局记录的remappedClasses()
哈希表中; - 将
cls
标记为 named class,以cls->mangledName()
类名为关键字添加到全局记录的gdb_objc_realized_classes
哈希表中,表示 runtime 开始可以通过类名查找类(注意元类不需要添加); - 将
cls
及其元类标记为 allocated class,并将两者均添加到全局记录的allocatedClasses
哈希表中(无需关键字),表示已为类分配固定内存空间;
注意:传入
readClass(...)
的cls
参数是Class
类型,而函数返回结果也是Class
,为什么读取类信息是“从类中读取类信息”这样怪异的过程呢?其实是因为cls
参数来源于 runtime 未开源的 从镜像(image)中读取类的过程,该过程输出的objc_class
存在特殊之处:要么输出 future class,要么输出普通类但是其bits
指向的是class_ro_t
结构体而非class_rw_t
,之所以如此是因为从镜像读取的是编译时决议的静态数据,本来就应该保存在class_ro_t
结构体中。
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
const char *mangledName = cls->mangledName();
//类的继承链上,存在既不是根类(RO_ROOT位为0)又没有超类的类,则为missingWeakSuperclass
//注意:这是唯一的向remappedClasses中添加nil值的入口
if (missingWeakSuperclass(cls)) {
addRemappedClass(cls, nil);
cls->superclass = nil;
return nil;
}
// 兼容旧版本libobjc的配置,可忽略
cls->fixupBackwardDeployingStableSwift();
Class replacing = nil;
if (Class newCls = popFutureNamedClass(mangledName)) {
// 已经全局记录该类名的 future class
// 构建newCls并将cls的内容拷贝到其中,保存future class的rw中的数据
// 以cls为关键字将构建的newCls添加到全局记录的remappedClasses哈希表中
class_rw_t *rw = newCls->data();
const class_ro_t *old_ro = rw->ro;
memcpy(newCls, cls, sizeof(objc_class));
rw->ro = (class_ro_t *)newCls->data();
newCls->setData(rw);
freeIfMutable((char *)old_ro->name);
free((void *)old_ro);
addRemappedClass(cls, newCls);
replacing = cls;
cls = newCls;
}
if (headerIsPreoptimized && !replacing) {
// 已存在该类名的named class
assert(getClassExceptSomeSwift(mangledName));
} else {
// 将类添加到 named classes
addNamedClass(cls, mangledName, replacing);
// 将类添加到 allocated classes
addClassTableEntry(cls);
}
// 设置RO_FROM_BUNDLE位
if (headerIsBundle) {
cls->data()->flags |= RO_FROM_BUNDLE;
cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
}
return cls;
}
从上文readClass(...)
代码if (Class newCls = popFutureNamedClass(mangledName))
分支内free((void *)old_ro)
语句,得出在cls
映射到newCls
过程中,完全丢弃了 future class 的ro
数据。最后,结合以上所有代码,可以归纳以下结论:
- Future class 类的有效数据实际上仅有:类名和
rw
。rw
中的数据作用也非常少,仅使用flags
的RO_FUTURE
(实际上就是RW_FUTURE
)标记类是 future class; - Future class 的作用是为指定类名的类,提前分配好内存空间,调用
readClass(...)
函数读取类时,才正式写入类的数据。 Future class 是用于支持类的懒加载机制;
3.1.2 remapped class
在上文 3.1.1 有提到类的重映射,重映射的类被标记为 remapped class,并以映射前的类为关键字,添加到全局的remappedClass
哈希表中。回顾Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
函数中,类的重映射代码如下,关于处理过程的详细描述已注释到代码中:
// 1. 若该类名已被标记为future class,则弹出该类名对应的future class 赋值给newCls
if (Class newCls = popFutureNamedClass(mangledName)) {
// 2. rw记录future class的rw
class_rw_t *rw = newCls->data();
// 3. future class的ro记为old_ro,后面释放其占用的内存空间并丢弃
const class_ro_t *old_ro = rw->ro;
// 4. 将cls中的数据拷贝到newCls,主要是要沿用cls的isa、superclass和cache数据
memcpy(newCls, cls, sizeof(objc_class));
// 5. rw记录cls的ro
rw->ro = (class_ro_t *)newCls->data();
// 6. 沿用future class的rw、cls的ro
newCls->setData(rw);
// 7. 释放future class的ro占用的空间
freeIfMutable((char *)old_ro->name);
free((void *)old_ro);
// 8. 将newCls以cls为关键字添加到remappedClasses哈希表中
addRemappedClass(cls, newCls);
replacing = cls;
cls = newCls;
}
综合上面代码的详细注释,可知cls
重映射到newCls
后,newCls
的数据保留了cls
中的superclass
、cache
成员,但是bits
中指向class_rw_t
结构体地址的位域(FAST_DATA_MASK
)指向了新的class_rw_t
结构体。该结构体的ro
指针指向cls->data()
所指向的内存空间中保存的class_ro_t
结构体,其他数据则是直接沿用 从namedFutureClasses
哈希表中弹出的 future class 的class_rw_t
结构体(通过future class 的data()
方法返回)中数据。
注意:虽然
objc_class
的data()
方法声明为返回class_rw_t *
,但是究其本质,它只是返回了objc_class
的bits
成员的FAST_DATA_MASK
标记的位域中保存的内存地址,该内存地址实际上可以保存任何类型的数据。在Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
函数中,传入的cls
所指向的objc_class
结构体有其特殊之处:cls
的bits
成员的FAST_DATA_MASK
位域,指向的内存空间保存的是class_ro_t
结构体,并不是通常的class_rw_t
。
上述只是对 future class 的重映射过程,通用的类重映射调用static class remapClass(Class cls)
,注意当传入的cls
类不在remappedClasses
哈希表中时,直接返回cls
本身;static void remapClassRef(Class *clsref)
可对传入的Class* clsref
重映射(改变*clsref
的值),返回时clsref
将 指向*clsref
重映射后的类。类的重映射相关代码如下:
// 获取remappedClasses,保存已重映射的所有类的全局哈希表
static NXMapTable *remappedClasses(bool create)
{
// 静态的全局哈希表,没有找到remove接口,只会无限扩张
static NXMapTable *remapped_class_map = nil;
runtimeLock.assertLocked();
if (remapped_class_map) return remapped_class_map;
if (!create) return nil;
// remapped_class_map is big enough to hold CF’s classes and a few others
INIT_ONCE_PTR(remapped_class_map,
NXCreateMapTable(NXPtrValueMapPrototype, 32),
NXFreeMapTable(v));
return remapped_class_map;
}
// 将oldcls重映射得到的newcls,以oldcls为关键字插入到remappedClasses哈希表中
// 注意:从代码透露出来的信息是,remappedClasses中只保存 future class 重映射的类
static void addRemappedClass(Class oldcls, Class newcls)
{
runtimeLock.assertLocked();
if (PrintFuture) {
_objc_inform("FUTURE: using %p instead of %p for %s",
(void*)newcls, (void*)oldcls, oldcls->nameForLogging());
}
void *old;
old = NXMapInsert(remappedClasses(YES), oldcls, newcls);
assert(!old);
}
// 获取cls的重映射类
// 注意:当remappedClasses为空或哈希表中不存在`cls`关键字,是返回`cls`本身,否则返回`cls`重映射后的类
static Class remapClass(Class cls)
{
runtimeLock.assertLocked();
Class c2;
if (!cls) return nil;
NXMapTable *map = remappedClasses(NO);
if (!map || NXMapMember(map, cls, (void**)&c2) == NX_MAPNOTAKEY) {
return cls;
} else {
return c2;
}
}
// 对Class的指针的重映射,返回时传入的clsref将 指向*clsref重映射后的类
static void remapClassRef(Class *clsref)
{
runtimeLock.assertLocked();
Class newcls = remapClass(*clsref);
if (*clsref != newcls) *clsref = newcls;
}
最后归纳出以下结论:
- Future class 重映射返回新的类,保存在
remappedClasses
全局哈希表中; - 普通类重映射返回类本身;
- 重映射的真正的目的是支持类的懒加载,懒加载类暂存为 future class 只记录类名及 future class 属性,在调用
readClass
才正式载入类数据。
3.1.3 realized class
调用readClass(...)
读取类数据只是载入了类的class_ro_t
静态数据,因此仍需要进一步配置objc_class
的class_rw_t
结构体的数据。这个过程为 class realizing,姑且称之为认识类。具体包括:
- 配置
class_rw_t
的RW_REALIZED
、RW_REALIZING
位; - 根据
class_ro_t
的RO_META
位的值,配置class_rw_t
的version
; - 因为静态载入的父类、元类有可能被重映射,因此要保证类的父类、元类完成class realizing;
- 配置
class_rw_t
的superclass
; - 初始化
objc_class
的isa
指针; - 配置
ivarLayout
、instanceSize
、instanceStart
。该步骤非常重要,新版本 runtime 支持 non-fragile instance variables,类的instanceStart
、instanceSize
会根据父类的instanceSize
动态调整,且需要按 WORD 对齐(TODO:后续在独立的文章中详细介绍); - 配置
class_rw_t
的RO_HAS_CXX_STRUCTORS
、RO_HAS_CXX_DTOR_ONLY
、RW_FORBIDS_ASSOCIATED_OBJECTS
; - 添加子类/根类;
- 将
class_ro_t
中的基本方法列表、属性列表、协议列表,类的分类(category)中的方法列表等信息添加到class_rw_t
中(TODO:后续在独立的文章中详细介绍);
实现 class realizing 的代码主要在static Class realizeClassWithoutSwift(Class cls)
函数中,只需要知道其大致过程即可。具体代码及注释如下:
static Class realizeClassWithoutSwift(Class cls)
{
runtimeLock.assertLocked();
const class_ro_t *ro;
class_rw_t *rw;
Class supercls;
Class metacls;
bool isMeta;
if (!cls) return nil;
if (cls->isRealized()) return cls;
assert(cls == remapClass(cls)); // 传入的类必须不存在于remappedClasses全局哈希表中
ro = (const class_ro_t *)cls->data();
if (ro->flags & RO_FUTURE) {
// 若为future class,则cls的rw指向class_rw_t结构体,ro指向class_ro_t结构体,维持原状
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// 普通类,则需要为rw分配内存,并将ro指针指向 传入的cls->data()所指向的内存空间
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
isMeta = ro->flags & RO_META;
rw->version = isMeta ? 7 : 0; // old runtime went up to 6
// 忽略
cls->chooseClassArrayIndex();
// 父类realizing
supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
// 元类realizing
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
#if SUPPORT_NONPOINTER_ISA
// 配置RW_REQUIRES_RAW_ISA位。可忽略。
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
if (DisableNonpointerIsa) {
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && !(ro->flags & RO_META) &&
0 == strcmp(ro->name, "OS_object"))
{
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->superclass &&
supercls->instancesRequireRawIsa())
{
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
// 配置RW_REQUIRES_RAW_ISA位
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsa(rawIsaIsInherited);
}
#endif
// 由于存在class remapping的可能性,因此需要更新父类及元类
cls->superclass = supercls;
cls->initClassIsa(metacls);
// 调整ivarLayout
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// 调整instanceSize
cls->setInstanceSize(ro->instanceSize);
// 忽略
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// 忽略
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// 添加子类/根类
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// rw中需要保存ro中的一些数据,例如ro中的基础方法列表、属性列表、协议列表
// rw还需要载入分类的方法列表
methodizeClass(cls);
return cls;
}
static void methodizeClass(Class cls)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro;
// 将ro中的基本方法列表添加到rw的方法列表中
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
rw->methods.attachLists(&list, 1);
}
// 将ro中的属性列表添加到rw的属性列表中
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rw->properties.attachLists(&proplist, 1);
}
// 将ro中的协议列表添加到rw的协议列表中
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rw->protocols.attachLists(&protolist, 1);
}
if (cls->isRootMetaclass()) {
// 根元类特殊处理
addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
}
// 将分类中的方法列表添加到rw的方法列表中
category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
attachCategories(cls, cats, false /*don't flush caches*/);
if (PrintConnecting) {
if (cats) {
for (uint32_t i = 0; i < cats->count; i++) {
_objc_inform("CLASS: attached category %c%s(%s)",
isMeta ? '+' : '-',
cls->nameForLogging(), cats->list[i].cat->name);
}
}
}
if (cats) free(cats);
}
最后总结,截止至完成 class realizing,类的加载过程大致如下图所示。其中future class列是懒加载类(future class)的流程,经过了“添加懒加载类->加载懒加载类信息->懒加载类重映射->认识懒加载类”四步;normal class列是普通的非懒加载类的加载流程,只经过“加载类信息->认识类”两个步骤。

类完成 class realizing 后,还需要执行类及分类中的load()
方法,最后在程序运行过程中第一次调用类的方法时(实现逻辑在IMP lookUpImpOrForward(...)
函数中)触发isInitialized()
检查,若未初始化,则需要先执行类的initialize()
方法。至此,类正式加载完成。
注意:最后的 class initializing 严格意义上应该不属于类的加载过程,可以将其归为独立的类初始化阶段。类的加载在
load()
方法执行后就算是完成了。
3.2 基本状态相关行为
objc_class
结构体中类的基本状态查询的函数代码如下。注意Class getMeta()
获取元类时:对于元类,getMeta()
返回的结果与ISA()
返回的结果不相同,对于非元类,两者则是相同的。
bool isARC() {
return data()->ro->flags & RO_IS_ARC;
}
bool isMetaClass() {
assert(this);
assert(isRealized());
return data()->ro->flags & RO_META;
}
bool isMetaClassMaybeUnrealized() {
return bits.safe_ro()->flags & RO_META;
}
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
bool isRootClass() {
return superclass == nil;
}
bool isRootMetaclass() {
return ISA() == (Class)this;
}
const char *mangledName() {
assert(this);
if (isRealized() || isFuture()) {
return data()->ro->name;
} else {
return ((const class_ro_t *)data())->name;
}
}
const char *demangledName();
const char *nameForLogging();
3.3 内存分配相关行为
根据类的信息构建对象时,需要根据类的继承链上的所有成员变量的内存布局为成员变量数据分配内存空间,分配内存空间的大小固定的,并按 WORD 对齐,调用size_t class_getInstanceSize(Class cls)
实际是调用了objc_class
结构体的uint32_t alignedInstanceSize()
函数。
成员变量在实例内存空间中偏移量同样也是固定的,同样也是按 WORD 对齐。实例的第一个成员变量内存空间的在实例空间中的偏移量,实际是通过调用objc_class
结构体的uint32_t alignedInstanceStart()
函数获取。
objc_class
结构体中涉及内存分配的函数代码如下:
// 类的实例的成员变量起始地址可能不按WORD对齐
uint32_t unalignedInstanceStart() {
assert(isRealized());
return data()->ro->instanceStart;
}
// 配置类的实例的成员变量起始地址按WORD对齐
uint32_t alignedInstanceStart() {
return word_align(unalignedInstanceStart());
}
// 类的实例大小可能因为ivar的alignment值而不按WORD对齐
uint32_t unalignedInstanceSize() {
assert(isRealized());
return data()->ro->instanceSize;
}
// 配置类的实例大小按WORD对齐
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
// 获取类的实例大小
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes. (TODO:不懂为啥)
if (size < 16) size = 16;
return size;
}
// 配置类的实例大小
void setInstanceSize(uint32_t newSize) {
assert(isRealized());
if (newSize != data()->ro->instanceSize) {
assert(data()->flags & RW_COPIED_RO);
*const_cast<uint32_t *>(&data()->ro->instanceSize) = newSize;
}
bits.setFastInstanceSize(newSize);
}
四、对象
对象的数据结构是objc_object
结构体。objc_object
仅包含一个isa_t
类型的isa
指针,和<objc/runtime>
定义的objc_object
有所不同,后者的isa
指针是Class
(指向objc_class
结构体)。这是因为新版本 runtime 支持非指针类型isa
结构,非指针类型isa
不再是指向Class
的指针而是64位二进制位域,仅使用其中一部分位域保存对象的类的地址,其他位赋予特殊意义主要用于协助对象内存管理。
objc_object
包含的方法主要有以下几类:
isa
操作相关,isa
指向对象类型,在控制对象构建、对象成员变量访问、对象消息响应,对象内存管理方面有十分关键的作用,将在 4.1 中详细介绍isa
;- 关联对象(associated object)相关;
- 对象弱引用相关,对象释放后需要通知弱引用自动置
nil
,因此对象需要知晓所有弱引用的地址; - 引用计数相关,支持对象的引用计数(reference count)管理;
dealloc
相关,对象析构相关,主要是释放对关联对象的引用;- side table 相关,side table 是 runtime 管理对象内存的核心数据结构,包含对象内存引用计数信息、对象的弱引用信息等关键数据(TODO:后续在独立文章中介绍);
- 支持非指针类型
isa
相关;
对象的定义代码如下:
struct objc_object {
private:
isa_t isa;
public:
// 获取对象类型,建立在对象不是tagged pointer的假设上
Class ISA();
// 获取对象类型,对象可以是tagged pointer
Class getIsa();
// 初始化isa
void initIsa(Class cls /*nonpointer=false*/);
void initClassIsa(Class cls /*nonpointer=maybe*/);
void initProtocolIsa(Class cls /*nonpointer=maybe*/);
void initInstanceIsa(Class cls, bool hasCxxDtor);
// 设置isa指向新的类型
Class changeIsa(Class newCls);
// 对象isa是否为非指针类型
bool hasNonpointerIsa();
// TaggedPointer相关,忽略
bool isTaggedPointer();
bool isBasicTaggedPointer();
bool isExtTaggedPointer();
// 对象是否是类
bool isClass();
// 对象关联对象相关
bool hasAssociatedObjects();
void setHasAssociatedObjects();
// 对象弱引用相关
bool isWeaklyReferenced();
void setWeaklyReferenced_nolock();
//对象是否包含 .cxx 构造/析构函数
bool hasCxxDtor();
// 引用计数相关
id retain();
void release();
id autorelease();
// 引用计数相关的实现
id rootRetain();
bool rootRelease();
id rootAutorelease();
bool rootTryRetain();
bool rootReleaseShouldDealloc();
uintptr_t rootRetainCount();
// dealloc的实现
bool rootIsDeallocating();
void clearDeallocating();
void rootDealloc();
private:
void initIsa(Class newCls, bool nonpointer, bool hasCxxDtor);
id rootAutorelease2();
bool overrelease_error();
#if SUPPORT_NONPOINTER_ISA
// 支持非指针类型isa
id rootRetain(bool tryRetain, bool handleOverflow);
bool rootRelease(bool performDealloc, bool handleUnderflow);
id rootRetain_overflow(bool tryRetain);
bool rootRelease_underflow(bool performDealloc);
void clearDeallocating_slow();
void sidetable_lock();
void sidetable_unlock();
void sidetable_moveExtraRC_nolock(size_t extra_rc, bool isDeallocating, bool weaklyReferenced);
bool sidetable_addExtraRC_nolock(size_t delta_rc);
size_t sidetable_subExtraRC_nolock(size_t delta_rc);
size_t sidetable_getExtraRC_nolock();
#endif
// Side-table 相关操作
bool sidetable_isDeallocating();
void sidetable_clearDeallocating();
bool sidetable_isWeaklyReferenced();
void sidetable_setWeaklyReferenced_nolock();
id sidetable_retain();
id sidetable_retain_slow(SideTable& table);
uintptr_t sidetable_release(bool performDealloc = true);
uintptr_t sidetable_release_slow(SideTable& table, bool performDealloc = true);
bool sidetable_tryRetain();
uintptr_t sidetable_retainCount();
#if DEBUG
bool sidetable_present();
#endif
};
Tagged Pointer:对于一个对象引用(指针),一般情况下该引用的值为对象的内存地址,而tagged pointer则直接在地址中写入对象的类和数据。
4.1 对象的 isa
objc_object
的isa
主要用于标记对象的类型。新版本 runtime 的isa
支持两种形式:指针类型、非指针类型。前者简单指向对象的类。后者为64位二进制位域,当然其中也包括对象的类的地址,其他位域都有其特殊含义。为支持两种形式,runtime 使用isa_t
联合体保存对象的isa
。
注意:Union 联合体的成员之间共享内存空间。以
isa_t
为例,cls
成员和bits
成员虽然不同,但是两者的值实际在任何时候都是一致的。例如,isa.class = [NSString class]
指定了cls
指向NSString
类的内存地址,此时查看isa.bits
会发现其值为NSString
类的内存地址;反之,isa.bits = 0xFF
,则isa.class
的值也变为255
。
4.1.1 isa_t 联合体
isa_t
联合体有两个成员Class cls
、uintptr_t bits
,两者共享8个字节的内存空间(64位机)。以下为isa_t
的源代码,删除了其中 x86_64 及其他架构下的代码。
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
// 需要编译选项支持非指针类型isa
#if SUPPORT_NONPOINTER_ISA
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
uintptr_t indexed : 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 deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
# endif
// SUPPORT_NONPOINTER_ISA
#endif
};
下面是 arm64 架构下isa_t
的bits
的位域分布图示,左高位右低位。下面对各个位域的解析中有多次提及 side table(TODO:后续独立文章介绍),该结构用于对内存中的所有 Objective-C 对象进行统一的内存管理,其中最重要的是对象内存计数管理、对象弱指针管理。
indexed
:洋红区域右起第1位。0
表示isa
为指针类型,存储类的地址;1
表示isa
为非指针类型。之所以使用最低位区分isa
类型,是因为当isa
为Class
时,其本质是指向objc_class
结构体首地址的指针,由于objc_class
必定按WORD对齐,即地址必定是8的整数倍,因此指针类型的isa
的末尾3位必定全为0
;has_assoc
:洋红区域右起第2位。标记对象是否存在关联对象;has_cxx_dtor
:洋红区域右起第3位。标记对象是否存在cxx
语系的析构函数。使用指针类型isa
的对象,该标记保存在 side table 中;shiftcls
:红色区域共33位。保存类的虚拟内存地址,标记对象的类型(核心数据);magic
:黄色区域共6位。用于非指针类型的isa
校验,arm64架构下这6位为固定值0x1a
;weakly_referenced
:青色区域右起第1位。标记对象是否被弱引用。使用指针类型isa
的对象,该标记保存在 side table 中;deallocating
:青色区域右起第2位。标记对象是否已执行析构。使用指针类型isa
的对象,该标记保存在 side table 中;has_sidetable_rc
:青色区域右起第3位。标记是否联合 side table 保存该对象的引用计数;extra_rc
:绿色区域共19位。记录对象引用计数,在has_sidetable_rc
为1
时,需要联合 side table 才能获取对象的确切引用计数;

注意:MSB是Most Significant Bit指最高有效位,
extra_rc
需要处理上溢出情况因此为MSB,LSB是Least Significant Bit,indexed
位用来判断isa
指针的类型因此为LSB。
4.1.2 对象的 isa 相关操作
4.1.2.1 isa 的构建
对象构建时,需要直接或间接调用objc_object
的initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
构建isa
,其他initIsa
方法均在内部调用了该方法。其中cls
参数表示对象的类,nonpointer
表示是否构建非指针类型isa
,hasCxxDtor
表示对象是否存在cxx
语系析构函数。
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
assert(!isTaggedPointer());
if (!nonpointer) {
// 构建非指针类型isa
isa.cls = cls;
} else {
// 构建非指针类型isa
assert(!DisableNonpointerIsa);
assert(!cls->instancesRequireRawIsa());
isa_t newisa(0);
#if SUPPORT_INDEXED_ISA
assert(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
// 当前主流机型一般会运行到这个逻辑分支
newisa.bits = ISA_MAGIC_VALUE; // magic设置为0xA1,index设置为1
newisa.has_cxx_dtor = hasCxxDtor;
// shiftcls位域保存对象的类的地址,注意最低3位不需要保存,因为必定是全0
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
// 指向新的newisa
isa = newisa;
}
}
4.1.2.2 isa 的应用
非指针类型isa
实际是将 side table 中部分内存管理数据(包括部分内存引用计数、是否包含关联对象标记、是否被弱引用标记、是否已析构标记)转移到isa
中,从而减少objc_object
中内存管理相关操作的 side table 查询数量。objc_object
中关于状态查询的方法,大多涉及到isa
的位操作。方法数量有点多不一一列举,本节只以弱引用相关查询为例。
-
isWeaklyReferenced()
用于查询对象是否被弱引用。当对象isa
为非指针类型时,直接返回isa.weakly_referenced
,否则需要调用sidetable_isWeaklyReferenced ()
从 side table 中查询结果; -
setWeaklyReferenced_nolock()
用于设置对象是否被弱引用。当对象isa
为非指针类型时,仅需将weakly_referenced
位置为1
,否则需要调用sidetable_setWeaklyReferenced_nolock()
从 side table 中查询结果并写入。
inline bool
objc_object::isWeaklyReferenced()
{
assert(!isTaggedPointer());
if (isa.nonpointer) return isa.weakly_referenced;
else return sidetable_isWeaklyReferenced();
}
inline void
objc_object::setWeaklyReferenced_nolock()
{
// 源代码设置weakly_referenced过程比较繁杂
retry:
isa_t oldisa = LoadExclusive(&isa.bits);
isa_t newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
sidetable_setWeaklyReferenced_nolock();
return;
}
if (newisa.weakly_referenced) {
ClearExclusive(&isa.bits);
return;
}
newisa.weakly_referenced = true;
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
}
4.2 对象的构建
对象的构建本质上都通过调用_class_createInstanceFromZone(...)
函数实现,其中最关键的传入参数是Class
类型的cls
,含义是构建类为cls
的对象。代码看起来挺长,实际上仅包含两个操作:
- 为对象分配
cls->instanceSize()
大小的内存空间; - 构建对象的
isa
;
static __attribute__((always_inline))
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
if (!cls) return nil;
assert(cls->isRealized());
bool hasCxxCtor = cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (!zone && fast) {
// ----------- 逻辑分支1 ----------- //
// 1.1 分配对象内存
obj = (id)calloc(1, size);
if (!obj) return nil;
// 1.2 构建对象isa
obj->initInstanceIsa(cls, hasCxxDtor);
}
else {
// ----------- 逻辑分支2 ----------- //
// 2.1 分配对象内存
if (zone) {
obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (!obj) return nil;
// 2.2 构建对象isa
obj->initIsa(cls);
}
// 若存在cxx语系构造函数,则调用。可忽略
if (cxxConstruct && hasCxxCtor) {
obj = _objc_constructOrFree(obj, cls);
}
return obj;
}
4.3 对象的析构
对象的析构调用对象的rootDealloc()
方法,源代码虽然不很多但是整个过程经过了几个函数。总结对象析构所需要的操作如下:
- 释放对关联对象的引用;
- 清空 side table 中保存的该对象弱引用地址、对象引用计数等内存管理数据;
- 释放对象占用的内存;
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
// 释放对象占用内存
free(this);
}
else {
object_dispose((id)this);
}
}
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
// 释放对象占用内存
free(obj);
return nil;
}
void *objc_destructInstance(id obj)
{
if (obj) {
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// 若存在cxx语系析构函数,则调用。可忽略
if (cxx) object_cxxDestruct(obj);
// 释放关联对象
if (assoc) _object_remove_assocations(obj);
// 清空side table中保存的该对象的弱引用地址、及引用计数等内存管理数据
obj->clearDeallocating(); // 可忽略实现细节
}
return obj;
}
五、总结
-
Runtime 中对象用
objc_object
实现,用isa_t
类型占用8字节内存空间的isa
成员指向对象的类,新版本 runtime 的isa
还保存了对象引用计数、是否已析构、是否被弱引用、是否存在关联对象等对象内存管理的关键数据; -
Runtime 中类用
objc_class
实现,类也是一个对象,objc_class
的isa
指向类的元类,元类的isa
指向根元类,根元类的isa
指向根元类自身,该条件可以用于判断类是否为根元类; -
objc_class
的superclass
成员指向类的父类,用于组织类的继承链; -
objc_class
的数据保存在bits
成员的有效位域指向的内存空间中,类的编译时决议数据保存在class_ro_t
结构体中,运行时决议数据保存在class_rw_t
结构体中,class_ro_t
、class_rw_t
的flags
成员用于标记类的状态,数据总入口为class_rw_t
; -
类存在懒加载机制,懒加载类先标记为 future class,正式加载 future class 数据需要调用
readClass(...)
方法,对 future class 进行重映射(remapping); -
从镜像加载的类由于只包含编译时决议数据,因此
bits
成员指向class_ro_t
数据结构。必须经过 class realizing,构建类的class_rw_t
数据,调整类的instanceSize
、instanceStart
、ivarLayout
(为了支持 non-fragile instance variables),以及将class_ro_t
中的基本方法列表、属性列表、协议列表,类的分类(category)中的方法列表等信息添加到class_rw_t
中; -
类的成员变量、方法列表、属性列表、分类的实现及加载、对象内存管理涉及的 side table 将在后续独立文章中详细介绍。