reailzeClassWithoutSwift
上节我们主要研究了dyld到_objc_init,以及read_image。
read_image主要做了类加载过程中一些错误的修复和异常机制的控制,同时会将类和类的名称进行关联,更新gdb_objc_realized_classes所有类的哈希表和添加到已经加载的类列表中。
类的rw和ro的处理我们还没看到,本次我们就通过reailzeClassWithoutSwift来探究下MachO中的信息是如何加载到类中的。
reailzeClassWithoutSwift入口
入口在read_image中的类加载部分,源码如下:
// Category discovery MUST BE Late to avoid potential races// when other threads call the new category code before// this thread finishes its fixups.// +load handled by prepare_load_methods()// Realize non-lazy classes (for +load methods and static instances)for (EACH_HEADER) {classref_t const *classlist = hi->nlclslist(&count);for (i = 0; i < count; i++) {Class cls = remapClass(classlist[i]);if (!cls) continue;addClassTableEntry(cls);if (cls->isSwiftStable()) {if (cls->swiftMetadataInitializer()) {_objc_fatal("Swift class %s with a metadata initializer ""is not allowed to be non-lazy",cls->nameForLogging());}// fixme also disallow relocatable classes// We can't disallow all Swift classes because of// classes like Swift.__EmptyArrayStorage}realizeClassWithoutSwift(cls, nil);}}
从read_iamge源码中的注释可以知道,这段代码是对非懒加载的类的初始化操作。
我们创建的类和默认都是属于懒加载类,那么什么是非懒加载的类呢,我们只需要在类中实现+load方法就可以了。
- 非懒加载的类实现了+load方法
- 通过nlclslist函数获取非懒加载类列表
- 对类进行便利处理,完成非懒加载类的初始化工作
- addClassTableEntry把类添加到内存列表中
-
reailzeClassWithoutSwift源码
```objectivec static Class realizeClassWithoutSwift(Class cls, Class previously) { runtimeLock.assertLocked();
class_rw_t *rw; Class supercls; Class metacls;
if (!cls) return nil; 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?
auto ro = (const class_ro_t *)cls->data(); auto isMeta = ro->flags & RO_META; 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 = objc::zalloc<class_rw_t>();rw->set_ro(ro);rw->flags = RW_REALIZED|RW_REALIZING|isMeta;cls->setData(rw);
}
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 availablecls->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);
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.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;if (DisableNonpointerIsa) {// Non-pointer isa disabled by environment or app SDK versioninstancesRequireRawIsa = true;}else if (!hackedDispatch && 0 == strcmp(ro->getName(), "OS_object")){// hack for libdispatch et al - isa also acts as vtable pointerhackedDispatch = 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 metaclassinstancesRequireRawIsa = true;rawIsaIsInherited = true;}if (instancesRequireRawIsa) {cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);}}
// SUPPORT_NONPOINTER_ISA
endif
// Update superclass and metaclass in case of remappingcls->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 rwif (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 listsif (supercls) {addSubclass(supercls, cls);} else {addRootClass(cls);}// Attach categoriesmethodizeClass(cls, previously);return cls;
}
<a name="tbLfY"></a>## ro、rw处理先看一下ro、rw的处理代码```objectivecauto ro = (const class_ro_t *)cls->data();auto isMeta = ro->flags & RO_META;// 判断是否是元类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 = objc::zalloc<class_rw_t>();rw->set_ro(ro);rw->flags = RW_REALIZED|RW_REALIZING|isMeta;cls->setData(rw);}
cls->data()从MachO中获取数据地址,强制转换为class_ro_t *结构体指针,同时初始化rw的空间,并复制一份ro到rw中存放。
- ro属于clean memony,在编译时即确定的内存空间,只读,在加载后不会发生改变的空间,包括类名称、方法、协议和实例变量的信息。
- rw属于dirty memony,rw是运行时的结构,可读可写,由于其动态性,可以往类中添加属性、方法、协议。在运行时会发生变更的内存。
- ro:read only-只读的,保留类的原始数据
- rw:read write-外界通过rw查找方法,首先判断是否存在rwe,有就从rwe中寻找方法,没有就从ro中寻找方法
rwe:2020年新增。由于rw中的数据和ro中的数据重复造成了内存浪费,而且只有在运行时修改了类的信息才会造成两者的数据产生差异。为了节省内容从rw中分离出会变化的rwe,创建rwe时首先会copy一份ro中的数据,然后再修改活新增数据。因此使用了runtime对类进行修改后才有rwe数据。
类的处理
父类和元类的处理
// 递归处理父类和元类supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
对于是否支持NONPOINTER_ISA的类进行处理,指针优化是指isa的末尾是1还是0,优化后isa末尾是1,未优化的isa末尾是0。
对于元类以及特殊的情况下的场景的一些类,无需开始指着优化。#if SUPPORT_NONPOINTER_ISAif (isMeta) {// 元类isa是纯指针 不做优化cls->setInstancesRequireRawIsa();} else {// isa是否是纯指针,flag中第13位bool instancesRequireRawIsa = cls->instancesRequireRawIsa();bool rawIsaIsInherited = false;static bool hackedDispatch = false;// 这个就是环境变量配置的 OBJC_DISABLE_NONPOINTER_ISA// OBJC_DISABLE_NONPOINTER_ISA为YES isa就是纯指针if (DisableNonpointerIsa) {// Non-pointer isa disabled by environment or app SDK versioninstancesRequireRawIsa = true;}// 判断是否是OS_object OS_object是纯指针else if (!hackedDispatch && 0 == strcmp(ro->getName(), "OS_object")){// hack for libdispatch et al - isa also acts as vtable pointerhackedDispatch = true;instancesRequireRawIsa = true;}// 父类是指针,并且父类还有父类,那么自己也要是纯指针 父类是纯指针// 那么当前isa要是纯指针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 metaclassinstancesRequireRawIsa = true;rawIsaIsInherited = true;}// 递归设置isa为纯指针,子类也设置为纯指针// 父类为纯指针,子类也为纯指针if (instancesRequireRawIsa) {cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);}}// SUPPORT_NONPOINTER_ISA#endif
递归实例化父类和元类
- 判断设置isa是否为纯指针
- 元类isa是纯指针
- 类的isa是否是纯指针取flags第13位
- 父类是纯指针,并且父类还有父类,那么自己也要是纯指针
- 递归设置isa为纯指针,子类也设置为纯指针
关联父类和元类
// 关联当前类的父类和元类 isa关系图cls->setSuperclass(supercls);cls->initClassIsa(metacls);
此时只是给类关联好了ro和rw的结构信息,但是rwe还未设置,接下来我们继续探索methodizeClass。
methodizeClass
methodizeClass源码
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 timeif (PrintConnecting) {_objc_inform("CLASS: methodizing class '%s' %s",cls->nameForLogging(), isMeta ? "(meta)" : "");}// Install methods and properties that the class implements itself.method_list_t *list = ro->baseMethods();if (list) {prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);if (rwe) rwe->methods.attachLists(&list, 1);}property_list_t *proplist = ro->baseProperties;if (rwe && proplist) {rwe->properties.attachLists(&proplist, 1);}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 metaclassaddMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);}// Attach categories.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);}}objc::unattachedCategories.attachToClass(cls, cls,isMeta ? ATTACH_METACLASS : ATTACH_CLASS);#if DEBUG// Debug: sanity-check all SELs; log method list contentsfor (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}
- ro、rw、rwe初始化,元类判断标记
- 获取方法列表
- 获取属性列表
- 获取协议列表
- 是否根元类,根元类加了initialize方法
- 分类处理
此段代码重点是prepareMethodLists方法,我们来看一下这个方法都做了什么。
先说下结果,此处的rwe为null,rwe并不是在此处创建的,后续我们通过断点调试的方式验证一下。
prepareMethodLists源码
static voidprepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,bool baseMethods, bool methodsFromBundle, const char *why){// ………… 省略for (int i = 0; i < addedCount; i++) {method_list_t *mlist = addedLists[i];ASSERT(mlist);// Fixup selectors if necessaryif (!mlist->isFixedUp()) {fixupMethodList(mlist, methodsFromBundle, true/*sort*/);}}// ………… 省略}
我们再研究objc_msgSend的慢速查找流程中,查询方法列表是采用二分法进行查找的,我们也讲了二分法是需要先将方法进行排序。那么方法的排序工作就是在这里fixupMethodList完成的。
