类的加载

作者: 脚踏实地的小C | 来源:发表于2020-01-11 00:46 被阅读0次

    我们将从_objc_init开始

    void _objc_init(void)
    {
        static bool initialized = false;
        if (initialized) return;
        initialized = true;
        
        // fixme defer initialization until an objc-using image is found?
        environ_init();
        tls_init();
        static_init();
        lock_init();
        exception_init();
    
        _dyld_objc_notify_register(&map_images, load_images, unmap_image);
    }
    

    environ_init():环境变量的初始化。
    tls_init():当前线程key的绑定。比如每个线程数据的析构函数;
    static_init():运行C++静态构造函数。

    static_init.png
           这个时候我们如果打个断点,会看到这个 count11 ,那么,这个 11 里面是否包含我们写的方法个数嘛?显然不是,它只是跟系统级别的构造函数有关。

    lock_init():是空实现。可能是预留、未开源、工厂重写,或者能够跟 C++ 的通用。
    exception_init():初始化libobjc的异常处理系统,注册相应异常的回调函数,监控回调。
          介绍了这么多的前戏,也该我们的主角_dyld_objc_notify_register(&map_images, load_images, unmap_image);登场了。
          其中_dyld_objc_notify_register仅供Objc运行时使用,注册处理程序,以便在映射、取消映射和初始化Objc图像时调用。
          &map_images:映射镜像文件,dyld将使用包含objc-image-info的镜像文件的数组回调mapped函数

    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);
    }
    void  map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                      const struct mach_header * const mhdrs[])
    {
        static bool firstTime = YES;
        header_info *hList[mhCount];
        uint32_t hCount;
        size_t selrefCount = 0;
        if (firstTime) {
            preopt_init();
        }
        hCount = 0;
        int totalClasses = 0;
        int unoptimizedTotalClasses = 0;
        {
            uint32_t i = mhCount;
            while (i--) {
                const headerType *mhdr = (const headerType *)mhdrs[i];
    
                auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
                if (!hi) {
                    continue;
                }
                if (mhdr->filetype == MH_EXECUTE) {
    #if __OBJC2__
                    size_t count;
                    _getObjc2SelectorRefs(hi, &count);
                    selrefCount += count;
                    _getObjc2MessageRefs(hi, &count);
                    selrefCount += count;
    #else
                    _getObjcSelectorRefs(hi, &selrefCount);
    #endif
                }
                hList[hCount++] = hi;
    
            }
        }
        if (firstTime) {
            sel_init(selrefCount);
            arr_init();
        }
        if (hCount > 0) {
            _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
        }
        firstTime = NO;
    }
    

          这里你会发现和源码不太一样,主要是做了一些精简,我们只要知道一段代码主要的内容一般是在if else判断、whiledo while循环等,或者有 通过返回值来确定这个函数的功能 的代码块。毕竟 快速筛选核心代码才是王道
          这块代码里主要是计算出当前的count,然后通过_read_images读镜像文件。

    那么_read_images里面会做些什么呢?

    1、加载所有类到类的gdb_objc_realized_classes表中。
    2、对所有类做重映射。
    3、将所有SEL都注册到namedSelectors表中。
    4、修复函数指针遗留。
    5、将所有Protocol都添加到protocol_map表中。
    6、对所有Protocol做重映射。
    7、初始化所有非懒加载的类,进行rw、ro等操作.
    8、遍历已标记的懒加载的类,并做初始化操作。
    9、处理所有Category,包括ClassMeta Class
    10、初始化所有未初始化的类。

    从宏观角度来分析,可分为以下七大部分

    1、第一次进来,需要一个容器来存储数据,所以开始创建表
         NXCreateMapTable所有类的表 -- 包括实现和没实现的
         NXCreateHashTable记录已经开辟后的类(和元类)的表
         两张表的目的有时候有些未初始化的类在当前的表(小)里找不到,尽管它在总表里面有加载分配,但它并不一定是已经初始化的表,主要是为了断开。为了能够精确使用,不用每次从总表里查找。
    2、类处理
    3、方法编号处理
    4、协议处理
    5、非懒加载处理
    6、待处理的类
    7、分类处理

    void _read_images {
        
        // 1:第一次进来 - 开始创建表
        // gdb_objc_realized_classes : 所有类的表 - 包括实现的和没有实现的
        // allocatedClasses: 包含用objc_allocateClassPair分配的所有类(和元类)的表。(已分配)
        if (!doneOnce) {
               doneOnce = YES;
            // 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);
        }
        
        // 2:类处理
        for (i = 0; i < count; i++) {
          Class cls = (Class)classlist[i];
          Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
        }
        
        // 3: 方法编号处理
        for (EACH_HEADER) {
            SEL *sels = _getObjc2SelectorRefs(hi, &count);
            UnfixedSelectors += count;
            for (i = 0; i < count; i++) {
              const char *name = sel_cname(sels[i]);
              sels[i] = sel_registerNameNoLock(name, isBundle);
            }
        }
    
        // 4: 协议处理
        for (EACH_HEADER) {
            extern objc_class OBJC_CLASS_$_Protocol;
            Class cls = (Class)&OBJC_CLASS_$_Protocol;
            NXMapTable *protocol_map = protocols();
            protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
            for (i = 0; i < count; i++) {
                readProtocol(protolist[i], cls, protocol_map,
                             isPreoptimized, isBundle);
            }
        }
        
        // 5: 非懒加载类处理
        for (EACH_HEADER) {
          classref_t *classlist =
              _getObjc2NonlazyClassList(hi, &count);
          addClassTableEntry(cls);
          realizeClassWithoutSwift(cls);
        }
        
        // 6: 待处理的类
        if (resolvedFutureClasses) {
            for (i = 0; i < resolvedFutureClassCount; i++) {
                Class cls = resolvedFutureClasses[i];
                if (cls->isSwiftStable()) {
                    _objc_fatal("Swift class is not allowed to be future");
                }
                realizeClassWithoutSwift(cls);
                cls->setInstancesRequireRawIsa(false/*inherited*/);
            }
            free(resolvedFutureClasses);
        }
        
        // 7:分类处理
       for (EACH_HEADER) {
           category_t **catlist =
               _getObjc2CategoryList(hi, &count);
           bool hasClassProperties = hi->info()->hasCategoryClassProperties();
           for (i = 0; i < count; i++) {
               category_t *cat = catlist[i];
               Class cls = remapClass(cat->cls);
           }
       }
    }
    

          Class cls = (Class)classlist[i];得到的cls是个地址,此时还没有类,只是从map中读取到这个类占用的地址,但是这个地址指向的空间还未确定。

    类处理.png

          Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);readClass没有ro、rw进行处理,只是加入到总表里,并且因为已经分配了地址,所以也插入到HashTable中。添加过的不会再次添加readClass的关键代码如下:

    addNamedClass(cls, mangledName, replacing);
    addClassTableEntry(cls);
    

         realizeClassWithoutSwift:只是对rw中的ro进行赋值,rw还是没处理。如图所示:

    realizeClassWithoutSwift.png realizeClassWithoutSwift_2.png

          然后通过以下代码进行当前类的父类、元类进行实现( 递归 )。当是supercls时, 下次父类、元类通过addSubclass集成对等关系(双向链表

    supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
    
    cls->superclass = supercls;
    cls->initClassIsa(metacls);
    
    if (supercls) {
        addSubclass(supercls, cls);
      } else {
        addRootClass(cls);
      }
    
    父类和元类.png

         最后通过methodizeClass这个真正的对rw进行处理,通过attachListsro中的复制到rw中。那么问题来了!

    为什么已经有了ro还要rw呢?

         ro是万年不变的代码,只能够读取。但是有时候我们要进行外部的调试处理,这时我们就需要一个rw能够进行动态的添加处理。

    attachLists是什么情况才会执行呢?

    1、添加方法addMethods
    2、添加属性_class_addProperty
    3、添加协议class_addProtocol
    4、分类的加载attachCategories

    attachLists具体是怎么实现?

    多对多

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

    0对一

     // 0 lists -> 1 list
    list = addedLists[0];
    

    一对多:

     // 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快,memmove慢。

    attachLists的实现.png

          处理时是将一维数组method_t打包成method_list_t直接插到二维数组method_array_t方便直接

    method_array_t.png

    总结

    1、readclass:判断是不是后期要处理的类,插入到表;

    2、realizeClassWithoutSwift读取class的data() -> ro/rw创建 -> 父类与元类的实现 -> 父类与元类的归属关系 -> 将此类链接到其超类的子类列表

    3、methodizeClass:把ro的数据写入到rw

    4、attachLists:有一对多0对一多对多三种情况。

    相关文章

      网友评论

        本文标题:类的加载

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