APP加载 类的加载

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 所以不存在方法覆盖的问题 都会调用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容