美文网首页
APP加载 类的加载

APP加载 类的加载

作者: FireStroy | 来源:发表于2020-07-02 18:33 被阅读0次

app的加载分析

应用程序的加载需要多个底层库的依赖
库就是可执行的代码的二进制 — 被操作系统写入内存
常见的库文件类型:静态库 .a .lib — 动态库:framework .so .dll
动静态库 加载区别:
静态库在链接阶段,会将汇编生成的目标与引用的库一起链接打包到可执行文件中。
动态库在程序编译时不会链接到目标代码中,而是在程序运行时才被载入。减小打包app大小,共享内容节约资源,动态更新(仅限官方)
比如 UIKit、libdispatch、libobjc.dyld.

编译的过程

编译链接.jpg

加载过程

app启动后由动态链接器:dyld来链接(链接的也是可执行二进制文件)。


app加载过程.jpg
  1. app启动。
  2. 加载libsystem 。
  3. Runtime向dyld注册回调函数。
  4. 加载新image
  5. 执行 map_images、load_images
  6. 调用main函数

在main()函数之前的事情 __前缀代表汇编

dyld -> libsystem init -> libdispatch -> objc init -> _dyld_objc_notify_register

void _objc_init(void)中会做一些初始化比如环境变量 异常函数注册
其中 _dyld_objc_notify_register(&map_images, load_images, unmap_image);注册了回调函数,拿到dyld加载的相关数据。
map_images ()内部依次map_images_nolock ()-->_read_images ()

map_images () -->_read_images ()

map_images中主要干了这些事:

  1. 类的初始化 realizeClass 设置rw、ro
  2. 分类的处理 分类的方法、协议、属性添加到类中
  3. 加载相应的哈希表
void _read_images {
    if (!doneOnce) {
           doneOnce = YES;
        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);
       }
   }
}
  • 1.类的处理
    readclass() 做了2个重要操作 addNamedClass()和addClassTableEntrt() 前者把类插入一张objc_realized_classes表后者把类插入到allocatedClasses表中。

  • 2.方法编号处理

  • 5.非懒加载类处理
    realizedClassWithoutSwift() 读取class的data() rw创建 rw->ro赋值
    父类和元类的实现 归属关系确定(父类指向等)
    方法属性以及分类的处理:attachmethod() 这个函数里面会读取ro的信息对rw的具体数据进行赋值 method_list_tproperty_list_tprotocol_list_t 加载分类信息attachCategories()
    那么懒加载类呢? 在第一次发送消息的时候会判断isRealized()这时候才会去实现类。
    如果子类是非懒加载类 那么父类也会被实现

    1. 分类的处理
      要注意的是 懒加载的分类方法是被编译期就写入到ro中
      那么非懒加载分类情况下:非懒加载类走正常流程把非懒加载类的信息添加到rw中。而懒加载的类则会被提前实现并把非懒加载类的信息添加到rw中。
      关于类的拓展:
      在编译的时候直接作为类的一部分 那么也就是说你没有.m文件那就不能添加类拓展
  • Load_Images()

前面讲过在_objc_init()map_images()用于处理类相关的事情 。接着分析 load_images()
断点停在load_images()入口时 类的load方法还没有开始调用
看源码

void
load_images(const char *path __unused, const struct mach_header *mh)
{
    // Return without taking locks if there are no +load methods here.
    if (!hasLoadMethods((const headerType *)mh)) return;
    recursive_mutex_locker_t lock(loadMethodLock);
    // Discover load methods
    {
        mutex_locker_t lock2(runtimeLock);
        //准备非懒加载类load方法,将其放在一个数组中
        prepare_load_methods((const headerType *)mh);
    }
   //遍历加载load方法
    call_load_methods();
}

非懒加载的类才有load方法的实现 这里用的一个局部代码块处理

  • prepare_load_methods((const headerType *)mh);
    内部先查找类的load方法(递归找了父类的load实现)
    然后add_class_to_loadable_list(cls)放在一个数组中
    同时cls->setInfo(RW_LOADED)
    查找分类中的load 然后add_categroy_to_loadable_list(cls)
    大致处理同上
    提醒一下 调用add_categroy_to_loadable_list(cls)之前 会执行一次
    realizeClassWithoutSwift(cls) 这也是分类在实现了load之后会导致相关的类会提前实现的一个原因

  • call_load_methods();

整理一下 在load_images()中会先把实现了load()的类和分类取出来放在数组中 然后分别循环从数组中取出类和分类中的load方法调用方法
注意 如果主类和分类都有 会先调用主类 然后调用分类的load()方法 并且 这里的调用是直接调用函数 不是走的msgsend 所以不存在方法覆盖的问题 都会调用

相关文章

  • java类加载器和jar路径解析

    一、类加载器基本原理 虚拟机提供了3种类加载器:Bootstrap类加载器、Ext类加载器、App类加载器。他们之...

  • APP加载 类的加载

    app的加载分析 应用程序的加载需要多个底层库的依赖库就是可执行的代码的二进制 — 被操作系统写入内存常见的库文...

  • 如何理解不同类加载器加载的类不可以互相调用

    不同类加载器加载的类不可以互相调用 专业术语:定义类加载器、初始类加载器 在java中加载器分为4种: 1、app...

  • 第一章 类加载过程

    要点 类加载过程 类加载器 一、类加载过程 1.类的加载过程 类的加载 .class文件过程分为:加载---->连...

  • 深入理解jvm类加载机制

    1.什么是类加载? 类加载机制一个很大的体系,包括类加载的时机,类加载器,类加载时机。 1.1类加载过程 加载器加...

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

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

  • java基础知识之java类加载器

    1. 什么是类加载器 类加载器就是用来加载类的东西!类加载器也是一个类:ClassLoader 类加载器可以被加载...

  • JVM类加载入门

    一 类加载顺序 class类加载-->验证-->准备--->解析--->初始化 class类加载:通过类加载器加载...

  • 深入浅出“类加载器”

    内容概述 “类加载”介绍 “类加载器”介绍 深入“类加载器” 深入“父亲委托机制” 一,“类加载”介绍 “加载”是...

  • jvm类加载器详解和如何打破双亲委派机制

    类加载过程: 项目启动的时候,并不是加载项目中的所有类,是在使用的时候加载,类加载器加载类的时候首先加载父类,所以...

网友评论

      本文标题:APP加载 类的加载

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