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

2020-01-28  本文已影响0人  Joker_King

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

我们的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);
    }

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

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

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);
    }
    // Attach categories
    methodizeClass(cls);

1.2.2 methodizeClass

// 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);
    }
    // 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]));
        }
    }

添加逻辑分为以下几步

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

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

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

存储方式

2、问题扩展。

attachLists在哪些地方会被调用?

上一篇下一篇

猜你喜欢

热点阅读