美文网首页
iOS 类的加载(非懒加载类)

iOS 类的加载(非懒加载类)

作者: Joker_King | 来源:发表于2020-01-28 12:56 被阅读0次

在我们日常的开发中我们最常见的就是类,那么我们我们声明的类是如何被系统加载进来的呢?接下来我们就围绕着这个话题进行探索。

我们的dyld在初始化主程序时来到_objc_init函数中并注册了相应的回调函数。

void _objc_init(void)
{
    //设置系统的环境变量。
    environ_init();
    //线程相关的处理。
    tls_init();
    //运行C ++静态构造函数。
    static_init();
    
    lock_init();
    //注册异常的回调。
    exception_init();
    //调用dyld的函数注册一个回调,并执行回调函数。
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}

map_images

处理由dyld映射的镜像文件。

void
map_images(unsigned count, const char * const paths[],
           const struct mach_header * const mhdrs[])
{
    mutex_locker_t lock(runtimeLock);
    return map_images_nolock(count, paths, mhdrs);
}

1、 _read_images初探

把镜像文件中的数据读取到内存中

void 
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                  const struct mach_header * const mhdrs[])
{
    //.......保留主要逻辑的代码
    if (hCount > 0) {
        _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
    }
    //.......保留主要逻辑的代码
}

1.1第一次进来 - 开始创建表

当我们的应用通过冷启动的方式启动时会执行这个流程

if (!doneOnce) {
        doneOnce = YES;
        //省略。。。
        //执行taggedPointers相关处理
        if (DisableTaggedPointers) {
            disableTaggedPointers();
        }
        
        initializeTaggedPointerObfuscator();
        //省略。。。
        // namedClasses
        // Preoptimized classes don't go in this table.
        // 4/3 is NXMapTable's load factor
        //创建两张表
        int namedClassesSize = 
            (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
        //只要不是共享缓存里面的类,无论是否实现,都会被存在这里。
        gdb_objc_realized_classes =
            NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
        //存储所有被开辟过的类,无论是元类还是类。
        allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
    }

这段代码的主要作用就是,为我们创建两张表来存储类。

  • gdb_objc_realized_classes只要不是共享缓存里面的类,无论是否实现,都会被存在这里。
  • allocatedClasses存储所有被开辟过的类,无论是元类还是类。
    gdb_objc_realized_classes表中的类可能会包含allocatedClasses表中的类。

1.2类的实现

for (EACH_HEADER) {
        classref_t *classlist =  _getObjc2NonlazyClassList(hi, &count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if (!cls) continue;
            //省略。。。
            //将已开辟过的类添加进allocatedClasses表中
            addClassTableEntry(cls);
            // 实现所有非懒加载的类(实例化类对象的一些信息,例如rw)
            realizeClassWithoutSwift(cls);
        }
    }

1.2.1 realizeClassWithoutSwift

  • cls中取出ro,并给rw开辟内存空间。
ro = (const class_ro_t *)cls->data();
    if (ro->flags & RO_FUTURE) {
        // 省略非正常流程的处理。
    } else {
        // Normal class. Allocate writeable class data.
        rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
        //将ro赋值给rw中的ro
        rw->ro = ro;
        rw->flags = RW_REALIZED|RW_REALIZING;
        //将rw设置进cls
        cls->setData(rw);
    }
  • 递归处理父类和元类。
    // 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->superclass));
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));

递归的结束条件在开头的部分。

    if (!cls) return nil;
  • 父类和元类的归属关系。
    // Update superclass and metaclass in case of remapping
    cls->superclass = supercls;
    cls->initClassIsa(metacls);
  • 将此类链接到父类的子类列表中。
    // Connect this class to its superclass's subclass lists
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }
  • ro中的数据复制到rw中。
    // Attach categories
    methodizeClass(cls);

1.2.2 methodizeClass

  • 复制类的方法列表,协议列表,和属性列表到rw中。
// Install methods and properties that the class implements itself.
    method_list_t *list = ro->baseMethods();
    if (list) {
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
        rw->methods.attachLists(&list, 1);
    }

    property_list_t *proplist = ro->baseProperties;
    if (proplist) {
        rw->properties.attachLists(&proplist, 1);
    }

    protocol_list_t *protolist = ro->baseProtocols;
    if (protolist) {
        rw->protocols.attachLists(&protolist, 1);
    }
  • 将分类中的方法列表,属性列表添加到rw中。
    // Attach categories.
    category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
    attachCategories(cls, cats, false /*don't flush caches*/);

1.2.3 attachLists

这是真正执行添加操作的地方。

void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;

        if (hasArray()) {
            // many lists -> many lists
            uint32_t oldCount = array()->count;//10
            uint32_t newCount = oldCount + addedCount;//4
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
            array()->count = newCount;// 10+4
   
            memmove(array()->lists + addedCount, array()->lists,
                    oldCount * sizeof(array()->lists[0]));
            
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
        else if (!list  &&  addedCount == 1) {
            // 0 lists -> 1 list
            list = addedLists[0];
        } 
        else {
            // 1 list -> many lists
            List* oldList = list;
            uint32_t oldCount = oldList ? 1 : 0;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)malloc(array_t::byteSize(newCount)));
            array()->count = newCount;
            if (oldList) array()->lists[addedCount] = oldList;
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
    }

添加逻辑分为以下几步

  • 多个list多多个list
    -- 计算总list的个数=已有list的个数+新增list的个数。
    -- 执行扩容操作,重新开辟内存。
    -- 将原来的list平移到数组的末尾。再将新增的list拷贝到数组的开头。(这一步也解释了为什么分类中的方法会先被查找到)。
  • 0list对1list
    -- 直接将新增的列表添加进去。
  • 1list对多个list
    -- 计算总list的个数=1+新增list的个数。
    -- 执行扩容操作,重新开辟内存。
    -- 将原来的list平移到数组的末尾。再将新增的list拷贝到数组的开头。(这一步也解释了为什么分类中的方法会先被查找到)。

memcpy函数:从源内存地址的起始位置开始拷贝若干个字节到新的目标内存地址中。

1.2.4 class_rw_t存储方法协议属性的方式

class_rw_t是以二位数组的方式来存储的,大致的形式如下。

存储方式

2、问题扩展。

attachLists在哪些地方会被调用?

  • 类的加载-处理方法属性协议methodizeClass
  • 动态添加方法-addMethods
  • 动态添加属性-_class_addProperty
  • 动态添加协议-class_addProtocol
  • 分类的加载-attachCategories

相关文章

  • iOS 类的加载(非懒加载类)

    在我们日常的开发中我们最常见的就是类,那么我们我们声明的类是如何被系统加载进来的呢?接下来我们就围绕着这个话题进行...

  • iOS底层原理19:类和分类的加载

    前面已经探究了类的加载流程,类分为懒加载类和非懒加载类,他们有不同加载流程,下面来探究下分类的加载,以及分类和类搭...

  • iOS底层--懒加载类/非懒加载类

    懒加载类和非懒加载类的区分很简单,就是看类有没有实现load方法 非懒加载类:在App启动时就开始对其进行实现,因...

  • iOS 懒加载类和非懒加载类

    一、非懒加载类 - 实现了类的load方法 1、我们知道在objc初始化代码 中间注册回调方法 map_image...

  • 十二、类的加载

    懒加载类: 别人不动,我不动 非懒加载类: 通过+load 提前加载 我们可以到平时懒加载通过消息转发触发 ,所以...

  • 类和分类的加载

    懒加载类和非懒加载类 在 类的加载 篇章里边 我们说了加载了类,分类,协议等等一些事情,注释我们可以看出来这里加载...

  • iOS---11---类和分类加载

    [toc] 类的加载 非懒加载类在运行时处理,懒加载编译期确定.区分:方式为load方法,把所有类的加载提前.看代...

  • OC底层原理14 - 类的加载之分类

    类的懒加载和非懒加载 在OC底层原理13 - 类的加载过程[https://www.jianshu.com/p/8...

  • OC底层原理探索—类的加载(3)

    上一篇我们探索了类的加载流程等一系列方法以及懒加载类和非懒加载类这节课我们来探索下分类的加载流程 分类的本质 首先...

  • iOS 类的加载分析 (中)

    非懒加载类和懒加载类 总纲领: OC底层探寻[https://www.jianshu.com/p/2fb69ff9...

网友评论

      本文标题:iOS 类的加载(非懒加载类)

      本文链接:https://www.haomeiwen.com/subject/lcurthtx.html