本章内容
- 类的实现源码解析
- 懒加载类与非懒加载类
- 对于类结构中的rw,ro,rwe的分析
本章目的
知道类什么时候加载的,对于我们的启动优化也有帮助,知道rw,ro,rwe的区别和诞生的原因,以及rwe何时赋值,对于了解运行时机制有帮助。对于类的加载,我们本文说的其实是类的实现(说类的加载也没错),为什么这么说后面会讲。
类的实现
我们都知道我们OC/(Swift,swift本身是没有runtime的,但他们都共用了objc,只是swift的类和OC实现不同。)与其他语言不同是因为在运行时我们可以改变类的结构,内存大小,把需要在编译时进行的放在了运行时等等。但是一门编程语言与其他语言不同但肯定也是有相同处,就是我们在编译时已经能确定的东西,例如类的成员变量,名字等等这些东西。 而对于类的实现我们还要借助 objc初始化所看到的关于类的实现所用到的方法分析。
提示:如果说,你在看这个小节对ro,rw,rwe有疑问,可以先看我下面的解释再看本小节
readClass 分析
在上一章的时候就你就可以看到,我对于read_images
的描述中3流程能大致知道该函数里,关于对类的处理做了哪些东西。就是从mach-o里面读取类列表,然后一个个去给类添加名字,并加入我们之前提到过的全局类表中。这个操作无论系统的类还是我们自己创建的都会经历
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
// 非懒加载类获取类名的方式,但是其实这里已经都能获取了,可以看里面实现方式是从ro读取的
const char *mangledName = cls->nonlazyMangledName();
if (missingWeakSuperclass(cls)) {
// No superclass (probably weak-linked).
// Disavow any knowledge of this subclass.
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING class '%s' with "
"missing weak-linked superclass",
cls->nameForLogging());
}
addRemappedClass(cls, nil);
cls->setSuperclass(nil);
return nil;
}
cls->fixupBackwardDeployingStableSwift();
Class replacing = nil;
if (mangledName != nullptr) {
// 在这里千万不要以为类实现中的ro赋值于rw的操作是在这。
// 可以自行验证,你会发现无论是系统还是自己创建的都没有走这里
if (Class newCls = popFutureNamedClass(mangledName)) {
// This name was previously allocated as a future class.
// Copy objc_class to future class's struct.
// Preserve future's rw data block.
if (newCls->isAnySwift()) {
_objc_fatal("Can't complete future class request for '%s' "
"because the real class is too big.",
cls->nameForLogging());
}
class_rw_t *rw = newCls->data();
const class_ro_t *old_ro = rw->ro();
memcpy(newCls, cls, sizeof(objc_class));
// Manually set address-discriminated ptrauthed fields
// so that newCls gets the correct signatures.
newCls->setSuperclass(cls->getSuperclass());
newCls->initIsa(cls->getIsa());
rw->set_ro((class_ro_t *)newCls->data());
newCls->setData(rw);
freeIfMutable((char *)old_ro->getName());
free((void *)old_ro);
addRemappedClass(cls, newCls);
replacing = cls;
cls = newCls;
}
}
if (headerIsPreoptimized && !replacing) {
// class list built in shared cache
// fixme strict assert doesn't work because of duplicates
// ASSERT(cls == getClass(name));
ASSERT(mangledName == nullptr || getClassExceptSomeSwift(mangledName));
} else {
if (mangledName) { //some Swift generic classes can lazily generate their names
// 将类地址与名字绑定起来
addNamedClass(cls, mangledName, replacing);
} else {
Class meta = cls->ISA();
const class_ro_t *metaRO = meta->bits.safe_ro();
ASSERT(metaRO->getNonMetaclass() && "Metaclass with lazy name must have a pointer to the corresponding nonmetaclass.");
ASSERT(metaRO->getNonMetaclass() == cls && "Metaclass nonmetaclass pointer must equal the original class.");
}
// 将绑定后的类加到全局大类表中
addClassTableEntry(cls);
}
// for future reference: shared cache never contains MH_BUNDLEs
if (headerIsBundle) {
cls->data()->flags |= RO_FROM_BUNDLE;
cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
}
return cls;
}
realizeClassWithoutSwift 类实现核心方法
这个方法就是对于类实现的核心方法了,任何类(懒加载类,非懒加载类)都要经过这个方法的洗礼,才能实现。走到这里,对于类的加载你可能仍然模糊,觉得没有一个确切的答案,不要着急,先看源码,分析。源码建议仔细看一遍,虽说不一定非要记住,但能知道大概做了些什么就很好了。
其中最关键的就是对rw的赋值等等,以及本类实现,连带着元类,父类,父类元类等都实现
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
if (!cls) return nil;
// 如果类已经实现就返回,证明流程错误,里面有个方法会进行assert
if (cls->isRealized()) {
validateAlreadyRealizedClass(cls);
return cls;
}
ASSERT(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
// 获取ro,这里的代表的是指针转换,你可以当做是强制类型。毕竟从mach-o里面读取的就是一串数字而已。
// 但是这里你可能会有疑问,ro = rw,结构体强转得保证类型一致才行。其实你不必担心这个,因为从ro 就是从磁盘中获取的
auto ro = (const class_ro_t *)cls->data();
// 看看是不是元类
auto isMeta = ro->flags & RO_META;
// 如果是futer class,就是在read_images方法的时候那些混乱类
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
// 此处是类的rw开辟
rw = objc::zalloc<class_rw_t>();
// ro 赋值于rw真正地方
rw->set_ro(ro);
// 标识类实现
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
// 不用说也知道什么意思
cls->setData(rw);
}
// 这是类的缓存初始化,就是消息查找的时候那个cache_t
cls->cache.initializeToEmptyOrPreoptimizedInDisguise();
#if FAST_CACHE_META
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
// Choose an index for this class.
// Sets cls->instancesRequireRawIsa if indexes no more indexes are available
cls->chooseClassArrayIndex();
if (PrintConnecting) {
_objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
cls->nameForLogging(), isMeta ? " (meta)" : "",
(void*)cls, ro, cls->classArrayIndex(),
cls->isSwiftStable() ? "(swift)" : "",
cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
}
// Realize superclass and metaclass, if they aren't already.
// This needs to be done after RW_REALIZED is set above, for root classes.
// This needs to be done after class index is chosen, for root metaclasses.
// This assumes that none of those classes have Swift contents,
// or that Swift's initializers have already been called.
// fixme that assumption will be wrong if we add support
// for ObjC subclasses of Swift classes.
// 从下面我们也可以得知当一个子类被实现,一直到其父类都会跟着实现
// 去实现它的父类
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
// 去实现它的元类
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
// nonpointer_isa,不用关心
#if SUPPORT_NONPOINTER_ISA
if (isMeta) {
// Metaclasses do not need any features from non pointer ISA
// This allows for a faspath for classes in objc_retain/objc_release.
// 我们看到元类的isa打印是其类的名字,因为这里
cls->setInstancesRequireRawIsa();
} else {
// Disable non-pointer isa for some classes and/or platforms.
// Set instancesRequireRawIsa.
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
//如果说你记得上一章我说的环境变量,OBJC_DISABLE_NONPOINTER_ISA
// 所以nonpointer_isa标识就是末尾为1,下面只是去设置
if (DisableNonpointerIsa) {
// Non-pointer isa disabled by environment or app SDK version
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && 0 == strcmp(ro->getName(), "OS_object"))
{
// hack for libdispatch et al - isa also acts as vtable pointer
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->getSuperclass() &&
supercls->instancesRequireRawIsa())
{
// This is also propagated by addSubclass()
// but nonpointer isa setup needs it earlier.
// Special case: instancesRequireRawIsa does not propagate
// from root class to root metaclass
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
// 看这里根据环境变量去设置。不需要关心
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
}
}
// SUPPORT_NONPOINTER_ISA
#endif
// Update superclass and metaclass in case of remapping
// 设置其父类,以及isa指向元类
cls->setSuperclass(supercls);
cls->initClassIsa(metacls);
// Reconcile instance variable offsets / layout.
// This may reallocate class_ro_t, updating our ro variable.
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// Set fastInstanceSize if it wasn't set already.
cls->setInstanceSize(ro->instanceSize);
// Copy some flags from ro to rw
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// Propagate the associated objects forbidden flag from ro or from
// the superclass.
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// Connect this class to its superclass's subclass lists
// 添加关系,如果有父类
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories
// 看苹果自身备注,就知道是与分类相关,但真的是这样吗?请看下面分析
// 不要关心参数previously,其实它可以当做是备用参数,对我们来讲无实际意义
methodizeClass(cls, previously);
return cls;
}
methodizeClass 分析
请记得previously这个参数无意义,请不要关心这个参数。我们在之前的方法已经对类的rw做了赋值操作,那么下面这个方法就是对类的协议,属性,方法等等做处理,至于做什么处理请看源码。
其实这个方法我们可以看出除了对方法的排序,主要是针对rwe的情况进行处理,而rwe究竟是什么,它是何时被创建,创建的条件是什么。其实从这个方法我们也能猜到一些,比方说属性,方法,协议等我们做了什么操作导致它会被创建,当然也包括了分类
static void methodizeClass(Class cls, Class previously)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();
// Methodizing for the first time
if (PrintConnecting) {
_objc_inform("CLASS: methodizing class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
// Install methods and properties that the class implements itself.
// 从ro 获取方法,这些方法是在编译时就已经确定的。
method_list_t *list = ro->baseMethods();
if (list) {
//对类的方法列表进行一些处理,如排序
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
// 当有rwe的时候去添加。
// 额外提示,list其实存放的是一个个指针,你可以认为是数组指针,它是多维的.
// 但是一般情况下都是一维数组指针,出现多维的原因是因为load方法。这里我不做过多解释,可以看我的分类加载的文章
if (rwe) rwe->methods.attachLists(&list, 1);
}
// 跟方法处理一致,如果说rwe被染指了就会进行一些操作
property_list_t *proplist = ro->baseProperties;
if (rwe && proplist) {
rwe->properties.attachLists(&proplist, 1);
}
// 跟方法处理一致,如果说rwe被染指了就会进行一些操作
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
rwe->protocols.attachLists(&protolist, 1);
}
// Root classes get bonus method implementations if they don't have
// them already. These apply before category replacements.
// 自己意会
if (cls->isRootMetaclass()) {
// root metaclass
addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
}
// Attach categories.
// previously为nil,所以不必纠结
if (previously) {
if (isMeta) {
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_METACLASS);
} else {
// When a class relocates, categories with class methods
// may be registered on the class itself rather than on
// the metaclass. Tell attachToClass to look for those.
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_CLASS_AND_METACLASS);
}
}
// 这个方法就有意思了,不知道是否记得runtime_init创建的两张表,其中一个。
// objc::unattachedCategories.init(32); 表1
// objc::allocatedClasses.init();表2
// 在分类加载的时候我会说明,我们看到这里就可以知道,类加载和分类加载也是有很多联系的
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
#if DEBUG
// Debug: sanity-check all SELs; log method list contents
for (const auto& meth : rw->methods()) {
if (PrintConnecting) {
_objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',
cls->nameForLogging(), sel_getName(meth.name()));
}
ASSERT(sel_registerName(sel_getName(meth.name())) == meth.name());
}
#endif
}
总结
看了类的实现,我相信你会大致知道类被加载的时候,系统帮我们做了哪些操作,比如rw的赋值,对于类的方法,属性,协议,分类等处理。但是你肯定会有疑惑,类是在何时进行加载的,它的加载与分类有什么关系,分类加载又是如何。上面我没做说明,先往下看
懒加载类与非懒加载类
为什么会有如此区分,什么是非懒加载类,什么是懒加载类。我直接先说出答案
懒加载类:类第一次触发消息发送的时候进行实现的。在消息慢速发送流程中你是否发现了realizeAndInitializeIfNeeded_locked
这样一个方法。它就是去实现类的。
非懒加载类:类或者其分类实现load方法,它会在pre-main前就进行实现。也就是在map_images
函数流程。
总结
其实对于类加载,在这时候说有点早,因为我们只研究了对于类的,并没有考虑分类情况。因为分类也会影响类的实现。但是虽然不完整,先做个小总结:
类在编译时就已经确定了其ro,这里的ro你可以当做类加载,但是类真正实现也就是加载到内存被我们所使用,是上面两种情况。load方法的时候是pre-main之前就进行加载并且包含其父类,元类等都被加载。当没有实现load方法的时候就是在消息第一次发送的时候加载。
为什么这么做?不知道你是否看过我之前的文章对于应用程序加载,如果说一开始就加载那些不必须的东西就会造成启动缓慢,所以对于程序加载都是懒加载方式,用一块加载一块,这样既省内存又优化了启动时间
rw, ro, rwe
这三个结构体是影响类的核心所在。
rw本质class_rw_t
,dirtyMemory,它包含了ro与rwe,也就是说,我们平时所用到的方法,协议,属性等都是从这里取出来的,类实现的时候默认是ro数据,要记得rw开辟的时候赋值操作。但是为什么说包含rwe呢?
ro本质class_ro_t
,cleanMemory,这是类的干净的东西,其编译时就能确定的,例如类名,成员变量,乃至一些方法、属性等,这些存在磁盘中,所以我们看上面篇章的时候类实现都是先从ro入手就是因为这些不可变,存在磁盘。
rwe本质class_rw_ext_t
相当于rw的扩展,因为运行时机制我们可以动态添加方法、属性、协议等以及分类操作都会影响类的结构也就是rw,所以在有这些操作的时候系统就会进行开辟rwe,进行创建。但是又不是每一个类都会有上面操作,所以这些类就不会去开辟rwe,避免去染指类的结构
问题
注册的类能利用运行时添加成员变量吗?没有注册的类呢?
什么是已注册的类,就是已经添加到类表中,就是大类表。注册的类是不能添加的,没有注册的不用想我们都可以添加。为什么呢?我们是怎么注册类的?是通过objc_registerClassPair
方法进行的,这个方法里面有个标识符RW_CONSTRUCTING,代表了类正在建设,而我们添加成员变量是怎么弄的?是通过方法class_addIvar
,看其源码就知道已经建设完成的类不能添加