类的加载

我们将从_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++静态构造函数。

类的加载_第1张图片
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中读取到这个类占用的地址,但是这个地址指向的空间还未确定。

类的加载_第2张图片
类处理.png

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

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

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

类的加载_第3张图片
realizeClassWithoutSwift.png
类的加载_第4张图片
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);
  }
类的加载_第5张图片
父类和元类.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慢。

类的加载_第6张图片
attachLists的实现.png

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

类的加载_第7张图片
method_array_t.png

总结

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

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

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

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

你可能感兴趣的:(类的加载)