IMG_5625.JPG

前言

  • rwe什么时候有值,分类attachCategories加载时有

    补充

    承接上篇类的加载原理(上)内容
    readClass中调用了addNamedClass,而addNamedClass调用了NXMapInsert,NXMapInsert为在table表中以key(类名)-value(地址)形式插入,此步骤也仅是可以通过名字拿到类地址,具体类是如何加载的呢?

    1. static void addNamedClass(Class cls, const char *name, Class replacing = nil)
    2. {
    3. runtimeLock.assertLocked();
    4. Class old;
    5. if ((old = getClassExceptSomeSwift(name)) && old != replacing) {
    6. inform_duplicate(name, old, cls);
    7. // getMaybeUnrealizedNonMetaClass uses name lookups.
    8. // Classes not found by name lookup must be in the
    9. // secondary meta->nonmeta table.
    10. addNonMetaClass(cls);
    11. } else {
    12. NXMapInsert(gdb_objc_realized_classes, name, cls);
    13. }
    14. ASSERT(!(cls->data()->flags & RO_META));
    15. // wrong: constructed classes are already realized when they get here
    16. // ASSERT(!cls->isRealized());
    17. }
    1. void *NXMapInsert(NXMapTable *table, const void *key, const void *value)

    类处理流程-重点

  • map_images -> map_images_nolock -> _read_images

1631580058573.jpg

  • read_class在非懒加载类列表处理过程中,调用了realizeClassWithoutSwift,在类的慢速查找lookupImpOrForward中也会调用这个函数,所以realizeClassWithoutSwift是核心

    1. // Realize non-lazy classes (for +load methods and static instances)
    2. for (EACH_HEADER) {
    3. classref_t const *classlist = hi->nlclslist(&count);
    4. for (i = 0; i < count; i++) {
    5. Class cls = remapClass(classlist[i]);
    6. if (!cls) continue;
    7. addClassTableEntry(cls);
    8. if (cls->isSwiftStable()) {
    9. if (cls->swiftMetadataInitializer()) {
    10. _objc_fatal("Swift class %s with a metadata initializer "
    11. "is not allowed to be non-lazy",
    12. cls->nameForLogging());
    13. }
    14. // fixme also disallow relocatable classes
    15. // We can't disallow all Swift classes because of
    16. // classes like Swift.__EmptyArrayStorage
    17. }
    18. realizeClassWithoutSwift(cls, nil);
    19. }
    20. }

    realizeClassWithoutSwift

    流程图-类的加载

  • lookUpImpOrForward慢速查找也会调用realizeClassWithoutSwift方法

  • 读取data,创建ro

1631502360649.jpg
查看ro的内存结构

  • auto ro = (constclass_ro_t *)cls->data()处下断点,查看ro的内存结构,此时ro只有一个地址,其baseMethodList等还是没有具体的数据
  • 类别买了房子,只写了名字,家具(有地址了)还没有放入
  • 具体是在methodizeClass -> prepareMethodLists -> fixupMethodList(name, addr)加入到方法列表中,再根据地址排序

    1. (lldb) p ro
    2. (const class_ro_t *) $0 = 0x00000001000082f0
    3. (lldb) p *$0
    4. (const class_ro_t) $1 = {
    5. flags = 0
    6. instanceStart = 8
    7. instanceSize = 8
    8. reserved = 0
    9. = {
    10. ivarLayout = 0x0000000000000000
    11. nonMetaclass = nil
    12. }
    13. name = {
    14. std::__1::atomic<const char *> = "CCPerson" {
    15. Value = 0x0000000100003f5f "CCPerson"
    16. }
    17. }
    18. baseMethodList = 0x0000000100008338
    19. baseProtocols = nil
    20. ivars = nil
    21. weakIvarLayout = 0x0000000000000000
    22. baseProperties = nil
    23. _swiftMetadataInitializer_NEVER_USE = {}
    24. }

    ro复制到rw

  • ro->rw的真正复制是在此时实现

    • rw = objc::zalloc();
    • rw->set_ro(ro);
    • rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
    • cls->setData(rw);
      1. if (ro->flags & RO_FUTURE) {
      2. // This was a future class. rw data is already allocated.
      3. rw = cls->data();
      4. ro = cls->data()->ro();
      5. ASSERT(!isMeta);
      6. cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
      7. } else {
      8. // Normal class. Allocate writeable class data.
      9. rw = objc::zalloc<class_rw_t>();
      10. rw->set_ro(ro);
      11. rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
      12. cls->setData(rw);
      13. }

      元类设置

  • 如果是元类,从NonpointerIsa其不需要任何信息,所以可以设置setInstancesRequireRawIsa为yes,当前前类在retain或release效率会更高

  • 如果不是元类,但在环境变量中禁用了NonpointerIsa,同样其instancesRequireRawIsa为yes,提高效率

    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. ...
    16. }

    继承链设置

    1. cls->setSuperclass(supercls);
    2. cls->initClassIsa(metacls);

    C++构造函数设置

    ```cpp if (ro->flags & RO_HAS_CXX_STRUCTORS) {

    1. cls->setHasCxxDtor();
    2. if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
    3. cls->setHasCxxCtor();
    4. }

    }

  1. <a name="LK3Kk"></a>
  2. #### 建立类、子类的关系链表
  3. ```cpp
  4. // Connect this class to its superclass's subclass lists
  5. if (supercls) {
  6. addSubclass(supercls, cls);
  7. } else {
  8. addRootClass(cls);
  9. }

添加类

  1. methodizeClass(cls, previously);

methodizeClass

  • 添加类是在methodizeClass中处理

    • 通过ro->baseMethods()获取methodList
    • 如果存在list则进入prepareMethodLists
    • prepareMethodList又会调用fixupMethodList
    • fixupMethodList

      • 遍历methodLlist,将其sel取出设置到sel_registerNameNoLock(已注册类)中
      • SortBySELAddress,根据地址排序方法Sort by selector address. ```cpp static void fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort) { runtimeLock.assertLocked(); ASSERT(!mlist->isFixedUp());

      // fixme lock less in attachMethodLists ? // dyld3 may have already uniqued, but not sorted, the list if (!mlist->isUniqued()) { mutex_locker_t lock(selLock);

      // Unique selectors in list. for (auto& meth : *mlist) {

      1. const char *name = sel_cname(meth.name());
      2. meth.setName(sel_registerNameNoLock(name, bundleCopy));

      } }

      // Sort by selector address. // Don’t try to sort small lists, as they’re immutable. // Don’t try to sort big lists of nonstandard size, as stable_sort // won’t copy the entries properly. if (sort && !mlist->isSmallList() && mlist->entsize() == method_t::bigSize) { method_t::SortBySELAddress sorter; std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter); }

      // Mark method list as uniqued and sorted. // Can’t mark small lists, since they’re immutable. if (!mlist->isSmallList()) { mlist->setFixedUp(); } } ```

      懒加载类和非懒加载类->load

  • 赖加载类,使用时再加载,节约内存,增加编译速度 ——> 按需加载

  • 如果类实现了+load方法,则就会走非懒加载类流程,进行上述类流程处理图,是一个耗时操作
  • 只有类或者其分类都未实现load方法,则类的加载会在其第一次发送消息时处理,通过lookupImpOrForward->realizeClassWithoutSwift
    • 将类中+load方法屏蔽,后在realizeClassWithoutSwift断点调试,可以验证此函数调用流程

image.png
音视频设备模块框架图 (3).jpg

分类

本质探索

在main函数中添加CCPerson的分类

  • clang -rewrite-objc main.m -o main.cpp 编译后的到CPP文件
  • 分类CCPerson(CC)被编译为_category_t结构体

    1. static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
    2. &_OBJC_$_CATEGORY_CCPerson_$_CC,
    3. };
  • 分类也是一个结构体类型

    • name,就是CC
    • cls,类名
    • instance_methods,class_methods 分类没有元类,所以这里有两个参数,在attachToClass中同样有两个调用方法
      • attachCategories(cls, list.array(), list.count(), otherFlags | ATTACH_CLASS);
      • attachCategories(cls->ISA(), list.array(), list.count(), otherFlags | ATTACH_METACLASS);
    • protocols,协议,
    • properties, 属性,
    • 无set、get方法,通过关联对象来添加属性
      1. struct _category_t {
      2. const char *name;
      3. struct _class_t *cls;
      4. const struct _method_list_t *instance_methods;
      5. const struct _method_list_t *class_methods;
      6. const struct _protocol_list_t *protocols;
      7. const struct _prop_list_t *properties;
      8. };
  • 此时是编译文件,还没有运行时,所以name等信息还不知道

    1. static struct _category_t _OBJC_$_CATEGORY_CCPerson_$_CC __attribute__ ((used, section ("__DATA,__objc_const"))) =
    2. {
    3. "CCPerson",
    4. 0, // &OBJC_CLASS_$_CCPerson,
    5. (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_CCPerson_$_CC,
    6. (const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_CCPerson_$_CC,
    7. 0,
    8. (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_CCPerson_$_CC,
    9. };

    引入 - categories

    添加分类时,rwe可以赋上值,核心重点attachCategories

  • methodizeClass()函数中,rwe存在时,才会调用attachToClass,而rwe是否存在取决于extAllocIfNeeded函数

  • extAllocIfNeeded全局搜索,都与runtime有关,都是在运行时加载
  • 调起attachCategories的地方
    • load_categories_nolock -> attachCategories
    • attachToClass -> attachCategories
      1. class_rw_ext_t *extAllocIfNeeded() {
      2. auto v = get_ro_or_rwe();
      3. if (fastpath(v.is<class_rw_ext_t *>())) {
      4. return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
      5. } else {
      6. return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
      7. }
      8. }
      未命名文件 (5).jpg