reailzeClassWithoutSwift

上节我们主要研究了dyld到_objc_init,以及read_image。
read_image主要做了类加载过程中一些错误的修复和异常机制的控制,同时会将类和类的名称进行关联,更新gdb_objc_realized_classes所有类的哈希表和添加到已经加载的类列表中。
类的rw和ro的处理我们还没看到,本次我们就通过reailzeClassWithoutSwift来探究下MachO中的信息是如何加载到类中的。

reailzeClassWithoutSwift入口

入口在read_image中的类加载部分,源码如下:

  1. // Category discovery MUST BE Late to avoid potential races
  2. // when other threads call the new category code before
  3. // this thread finishes its fixups.
  4. // +load handled by prepare_load_methods()
  5. // Realize non-lazy classes (for +load methods and static instances)
  6. for (EACH_HEADER) {
  7. classref_t const *classlist = hi->nlclslist(&count);
  8. for (i = 0; i < count; i++) {
  9. Class cls = remapClass(classlist[i]);
  10. if (!cls) continue;
  11. addClassTableEntry(cls);
  12. if (cls->isSwiftStable()) {
  13. if (cls->swiftMetadataInitializer()) {
  14. _objc_fatal("Swift class %s with a metadata initializer "
  15. "is not allowed to be non-lazy",
  16. cls->nameForLogging());
  17. }
  18. // fixme also disallow relocatable classes
  19. // We can't disallow all Swift classes because of
  20. // classes like Swift.__EmptyArrayStorage
  21. }
  22. realizeClassWithoutSwift(cls, nil);
  23. }
  24. }

从read_iamge源码中的注释可以知道,这段代码是对非懒加载的类的初始化操作。
我们创建的类和默认都是属于懒加载类,那么什么是非懒加载的类呢,我们只需要在类中实现+load方法就可以了。

  • 非懒加载的类实现了+load方法
  • 通过nlclslist函数获取非懒加载类列表
  • 对类进行便利处理,完成非懒加载类的初始化工作
  • addClassTableEntry把类添加到内存列表中
  • realizeClassWithoutSwift初始化类

    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()) {

    1. validateAlreadyRealizedClass(cls);
    2. 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) {

    1. // This was a future class. rw data is already allocated.
    2. rw = cls->data();
    3. ro = cls->data()->ro();
    4. ASSERT(!isMeta);
    5. cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);

    } else {

    1. // Normal class. Allocate writeable class data.
    2. rw = objc::zalloc<class_rw_t>();
    3. rw->set_ro(ro);
    4. rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
    5. cls->setData(rw);

    }

    cls->cache.initializeToEmptyOrPreoptimizedInDisguise();

if FAST_CACHE_META

  1. if (isMeta) cls->cache.setBit(FAST_CACHE_META);

endif

  1. // Choose an index for this class.
  2. // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
  3. cls->chooseClassArrayIndex();
  4. if (PrintConnecting) {
  5. _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
  6. cls->nameForLogging(), isMeta ? " (meta)" : "",
  7. (void*)cls, ro, cls->classArrayIndex(),
  8. cls->isSwiftStable() ? "(swift)" : "",
  9. cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
  10. }
  11. // Realize superclass and metaclass, if they aren't already.
  12. // This needs to be done after RW_REALIZED is set above, for root classes.
  13. // This needs to be done after class index is chosen, for root metaclasses.
  14. // This assumes that none of those classes have Swift contents,
  15. // or that Swift's initializers have already been called.
  16. // fixme that assumption will be wrong if we add support
  17. // for ObjC subclasses of Swift classes.
  18. supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
  19. metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

if SUPPORT_NONPOINTER_ISA

  1. if (isMeta) {
  2. // Metaclasses do not need any features from non pointer ISA
  3. // This allows for a faspath for classes in objc_retain/objc_release.
  4. cls->setInstancesRequireRawIsa();
  5. } else {
  6. // Disable non-pointer isa for some classes and/or platforms.
  7. // Set instancesRequireRawIsa.
  8. bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
  9. bool rawIsaIsInherited = false;
  10. static bool hackedDispatch = false;
  11. if (DisableNonpointerIsa) {
  12. // Non-pointer isa disabled by environment or app SDK version
  13. instancesRequireRawIsa = true;
  14. }
  15. else if (!hackedDispatch && 0 == strcmp(ro->getName(), "OS_object"))
  16. {
  17. // hack for libdispatch et al - isa also acts as vtable pointer
  18. hackedDispatch = true;
  19. instancesRequireRawIsa = true;
  20. }
  21. else if (supercls && supercls->getSuperclass() &&
  22. supercls->instancesRequireRawIsa())
  23. {
  24. // This is also propagated by addSubclass()
  25. // but nonpointer isa setup needs it earlier.
  26. // Special case: instancesRequireRawIsa does not propagate
  27. // from root class to root metaclass
  28. instancesRequireRawIsa = true;
  29. rawIsaIsInherited = true;
  30. }
  31. if (instancesRequireRawIsa) {
  32. cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
  33. }
  34. }

// SUPPORT_NONPOINTER_ISA

endif

  1. // Update superclass and metaclass in case of remapping
  2. cls->setSuperclass(supercls);
  3. cls->initClassIsa(metacls);
  4. // Reconcile instance variable offsets / layout.
  5. // This may reallocate class_ro_t, updating our ro variable.
  6. if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
  7. // Set fastInstanceSize if it wasn't set already.
  8. cls->setInstanceSize(ro->instanceSize);
  9. // Copy some flags from ro to rw
  10. if (ro->flags & RO_HAS_CXX_STRUCTORS) {
  11. cls->setHasCxxDtor();
  12. if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
  13. cls->setHasCxxCtor();
  14. }
  15. }
  16. // Propagate the associated objects forbidden flag from ro or from
  17. // the superclass.
  18. if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
  19. (supercls && supercls->forbidsAssociatedObjects()))
  20. {
  21. rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
  22. }
  23. // Connect this class to its superclass's subclass lists
  24. if (supercls) {
  25. addSubclass(supercls, cls);
  26. } else {
  27. addRootClass(cls);
  28. }
  29. // Attach categories
  30. methodizeClass(cls, previously);
  31. return cls;

}

  1. <a name="tbLfY"></a>
  2. ## ro、rw处理
  3. 先看一下ro、rw的处理代码
  4. ```objectivec
  5. auto ro = (const class_ro_t *)cls->data();
  6. auto isMeta = ro->flags & RO_META;
  7. // 判断是否是元类
  8. if (ro->flags & RO_FUTURE) {
  9. // This was a future class. rw data is already allocated.
  10. rw = cls->data();
  11. ro = cls->data()->ro();
  12. ASSERT(!isMeta);
  13. cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
  14. } else {
  15. // Normal class. Allocate writeable class data.
  16. rw = objc::zalloc<class_rw_t>();
  17. rw->set_ro(ro);
  18. rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
  19. cls->setData(rw);
  20. }

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数据。

    类的处理

    父类和元类的处理

    1. // 递归处理父类和元类
    2. supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
    3. metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

    对于是否支持NONPOINTER_ISA的类进行处理,指针优化是指isa的末尾是1还是0,优化后isa末尾是1,未优化的isa末尾是0。
    对于元类以及特殊的情况下的场景的一些类,无需开始指着优化。

    1. #if SUPPORT_NONPOINTER_ISA
    2. if (isMeta) {
    3. // 元类isa是纯指针 不做优化
    4. cls->setInstancesRequireRawIsa();
    5. } else {
    6. // isa是否是纯指针,flag中第13位
    7. bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
    8. bool rawIsaIsInherited = false;
    9. static bool hackedDispatch = false;
    10. // 这个就是环境变量配置的 OBJC_DISABLE_NONPOINTER_ISA
    11. // OBJC_DISABLE_NONPOINTER_ISA为YES isa就是纯指针
    12. if (DisableNonpointerIsa) {
    13. // Non-pointer isa disabled by environment or app SDK version
    14. instancesRequireRawIsa = true;
    15. }
    16. // 判断是否是OS_object OS_object是纯指针
    17. else if (!hackedDispatch && 0 == strcmp(ro->getName(), "OS_object"))
    18. {
    19. // hack for libdispatch et al - isa also acts as vtable pointer
    20. hackedDispatch = true;
    21. instancesRequireRawIsa = true;
    22. }
    23. // 父类是指针,并且父类还有父类,那么自己也要是纯指针 父类是纯指针
    24. // 那么当前isa要是纯指针
    25. else if (supercls && supercls->getSuperclass() &&
    26. supercls->instancesRequireRawIsa())
    27. {
    28. // This is also propagated by addSubclass()
    29. // but nonpointer isa setup needs it earlier.
    30. // Special case: instancesRequireRawIsa does not propagate
    31. // from root class to root metaclass
    32. instancesRequireRawIsa = true;
    33. rawIsaIsInherited = true;
    34. }
    35. // 递归设置isa为纯指针,子类也设置为纯指针
    36. // 父类为纯指针,子类也为纯指针
    37. if (instancesRequireRawIsa) {
    38. cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
    39. }
    40. }
    41. // SUPPORT_NONPOINTER_ISA
    42. #endif
  • 递归实例化父类和元类

  • 判断设置isa是否为纯指针
  • 元类isa是纯指针
  • 类的isa是否是纯指针取flags第13位
  • 父类是纯指针,并且父类还有父类,那么自己也要是纯指针
  • 递归设置isa为纯指针,子类也设置为纯指针

关联父类和元类

  1. // 关联当前类的父类和元类 isa关系图
  2. cls->setSuperclass(supercls);
  3. cls->initClassIsa(metacls);

此时只是给类关联好了ro和rw的结构信息,但是rwe还未设置,接下来我们继续探索methodizeClass。

methodizeClass

methodizeClass源码

  1. static void methodizeClass(Class cls, Class previously)
  2. {
  3. runtimeLock.assertLocked();
  4. bool isMeta = cls->isMetaClass();
  5. auto rw = cls->data();
  6. auto ro = rw->ro();
  7. auto rwe = rw->ext();
  8. // Methodizing for the first time
  9. if (PrintConnecting) {
  10. _objc_inform("CLASS: methodizing class '%s' %s",
  11. cls->nameForLogging(), isMeta ? "(meta)" : "");
  12. }
  13. // Install methods and properties that the class implements itself.
  14. method_list_t *list = ro->baseMethods();
  15. if (list) {
  16. prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
  17. if (rwe) rwe->methods.attachLists(&list, 1);
  18. }
  19. property_list_t *proplist = ro->baseProperties;
  20. if (rwe && proplist) {
  21. rwe->properties.attachLists(&proplist, 1);
  22. }
  23. protocol_list_t *protolist = ro->baseProtocols;
  24. if (rwe && protolist) {
  25. rwe->protocols.attachLists(&protolist, 1);
  26. }
  27. // Root classes get bonus method implementations if they don't have
  28. // them already. These apply before category replacements.
  29. if (cls->isRootMetaclass()) {
  30. // root metaclass
  31. addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
  32. }
  33. // Attach categories.
  34. if (previously) {
  35. if (isMeta) {
  36. objc::unattachedCategories.attachToClass(cls, previously,
  37. ATTACH_METACLASS);
  38. } else {
  39. // When a class relocates, categories with class methods
  40. // may be registered on the class itself rather than on
  41. // the metaclass. Tell attachToClass to look for those.
  42. objc::unattachedCategories.attachToClass(cls, previously,
  43. ATTACH_CLASS_AND_METACLASS);
  44. }
  45. }
  46. objc::unattachedCategories.attachToClass(cls, cls,
  47. isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
  48. #if DEBUG
  49. // Debug: sanity-check all SELs; log method list contents
  50. for (const auto& meth : rw->methods()) {
  51. if (PrintConnecting) {
  52. _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',
  53. cls->nameForLogging(), sel_getName(meth.name()));
  54. }
  55. ASSERT(sel_registerName(sel_getName(meth.name())) == meth.name());
  56. }
  57. #endif
  58. }
  • ro、rw、rwe初始化,元类判断标记
  • 获取方法列表
  • 获取属性列表
  • 获取协议列表
  • 是否根元类,根元类加了initialize方法
  • 分类处理

此段代码重点是prepareMethodLists方法,我们来看一下这个方法都做了什么。
先说下结果,此处的rwe为null,rwe并不是在此处创建的,后续我们通过断点调试的方式验证一下。

prepareMethodLists源码

  1. static void
  2. prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
  3. bool baseMethods, bool methodsFromBundle, const char *why)
  4. {
  5. // ………… 省略
  6. for (int i = 0; i < addedCount; i++) {
  7. method_list_t *mlist = addedLists[i];
  8. ASSERT(mlist);
  9. // Fixup selectors if necessary
  10. if (!mlist->isFixedUp()) {
  11. fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
  12. }
  13. }
  14. // ………… 省略
  15. }

我们再研究objc_msgSend的慢速查找流程中,查询方法列表是采用二分法进行查找的,我们也讲了二分法是需要先将方法进行排序。那么方法的排序工作就是在这里fixupMethodList完成的。