不能向编译后得到的类中增加实例变量!
能向运行时创建的类中添加实例变量!
-
因为编译后的类已经注册在runtime中,类结构体中的objc_ivar_list 实例变量的链表和instance_size实例变量的内存大小已经确定,同时runtime 会调用class_setIvarLayout 或 class_setWeakIvarLayout来处理strong weak引用,所以不能向存在的类中添加实例变量。
-
运行时创建的类是可以添加实例变量,调用 class_addIvar 函数,但是得在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。
在苹果文档中,对 class_addIvar 函数有下面一段话:
This function may only be called after objc_allocateClassPair(_:_:_:) and before objc_registerClassPair(_:). Adding an instance variable to an existing class is not supported.
The class must not be a metaclass. Adding an instance variable to a metaclass is not supported.
这个功能只能在 objc_allocateClassPair(_:_:_:) 之后和 objc_registerClassPair(_:) 之前调用。不支持将实例变量添加到现有的类。
该类不能是元类。不支持将实例变量添加到元类。
文档是说不能将此函数用于已有的类,必须是动态创建的类,为了能够知道为何会这样,我们需要翻阅一下苹果开源的 runtime 源码。
1.首先看一下关于 objc_allocateClassPair 函数的代码实现:
去除干扰代码,我们寻找到下面的函数调用链条:
objc_allocateClassPair -> objc_initializeClassPair_internal
// 下面的代码已经被我大部分剔除,只留下我们分析所需要用到的代码
static void objc_initializeClassPair_internal(Class superclass, const char *name, Class cls, Class meta)
{
// Set basic info
cls->data()->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED | RW_REALIZING;
meta->data()->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED | RW_REALIZING;
cls->data()->version = 0;
meta->data()->version = 7;
// RW_CONSTRUCTING 类已分配但还未注册
// RW_COPIED_RO class_rw_t->ro 来自 class_ro_t 结构的复制
// RW_REALIZED // class_t->data 的结构为 class_rw_t
// RW_REALIZING // 类已开始分配,但并未完成
// 以上几个宏都是对新类的class_rw_t结构设置基本信息
}
2.下面是class_addIvar的与我分析所需要的实现代码
/ 无关代码已经剔除
BOOL
class_addIvar(Class cls, const char *name, size_t size,
uint8_t alignment, const char *type)
{
if (!cls) return NO;
if (!type) type = "";
if (name && 0 == strcmp(name, "")) name = nil;
rwlock_writer_t lock(runtimeLock);
assert(cls->isRealized());
// No class variables
if (cls->isMetaClass()) {
return NO;
}
// Can only add ivars to in-construction classes.
if (!(cls->data()->flags & RW_CONSTRUCTING)) {
return NO;
}
}
// 重点在这最后一句,前面我们已经看到 objc_allocateClassPair 函数所分配的新类的flags位信息,在此处 & RW_CONSTRUCTING,必定为真,取反后跳过大括号向下执行。
3.已经存在的类,经过测试,flag位为 RW_REALIZED|RW_REALIZING,设置函数如下:
static Class realizeClass(Class cls)
{
runtimeLock.assertWriting();
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));
// fixme verify class is not in an un-dlopened part of the shared cache?
ro = (const class_ro_t *)cls->data();
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
}
所以在经过条件 !((RW_REALIZED | RW_REALIZING) & RW_CONSTRUCTING) 时返回NO。
以上便是对已有类不能使用 class_addIvar 函数的分析
网友评论