iOS App启动原理
在工程中可以看到main
函数是程序的入口,其实在启动之前系统做了很多的工作,比如fork子进程,调用exec函数等,app的启动离不开系统内核的支持,所以在这之前需要对iOS系统架构有个基本了解
iOS系统简介
Mac系统是基于Unix
内核的图形化操作系统,Mac OS
和iOS
系统架构层只有最上面的应用层框架部分有区别,Mac
使用的是Cocoa
框架, 而iOS
是Cocoa Toouch
框架,其余层的架构都是一样的
- 应用层: 手机上的图形应用,如
Spotlight
,SprintBoard
等 - 应用层框架: Cocoa框架,包括 Foundation,ApplicationKit,UIKit等,从功能上主要表现为系统ui,网络,文件存储,以及系统服务,他们大都基于核心框架层封装而来
- 核心框架层: 各种
Core
开头的framework,CoreMedia,CoreAnimation,CoreGraphics,OpenGL,CoreFoundataion… - Drawin层: 这部分是操作系统的核型,包括kernel,dirivers,bsd等
Nac OX系统是又BSD
和Mach
所改写的一个混合内核,名字叫Drawin
的操作系统,符合POSIX
标准的UNIX
和行. Mach
是苹果自研的为内核操作系统,主要包括进程调度管理,内存管理,而I/O操作
,网络
和文件
等则由开源的BSD系统适配而来,通过LibSystem
库可以访问到哦这些和行功能.
程序启动
这里把从应用图标被用户点击到app,到app进入前台,声明周期变为active状态统称为启动前
- Objc
- Initializers
初始化C++
构造函数,以及静态对象加载
主程序的MachO
文件由内核加载,之后dyld会负责动态库的加载,dyld根据MachO
文件中Load Commands
找到置顶的恶LC_LOAD_DYLIB
,将相关的dylib
加载到进程的空间
结合图例,分为以下这几个步骤:
- 系统为程序启动做准备,初始化进程资源
- 加载主程序的MachO并将控制权交给
dyld
dyld
递归的加载程序所需要的动态库dyld
对程序进程相关的依赖库进行rebase和binding操作,最终实现程序内部符号和三方库之间的绑定- objc setup,objc的load调用,方法,协议,分类创建
- 运行初初始化函数,主要是C++构造函数和静态对象
- 执行main函数,应用程序入口,开启mainRunLoop,创建appDelegate
- 应用程序启动完成调用AppDelegate的
didFinishLaunch
方法
从pre-main之前可以看出,下面这些因子会严重拉胯程序的启动时间
- 动态库加载越多,启动越慢。
- ObjC类越多,启动越慢
- C的constructor函数越多,启动越慢
- C++静态对象越多,启动越慢
- ObjC的+load越多,启动越慢
dyld动态库加载器
源码: https://opensource.apple.com/tarballs/dyld/,这里有多个版本
- 终端执行
man dyld
可以看到它的一些基本说名,它是系统的动态链接器,将外部动态库与主程序关联,完成他们之间的符号绑定
dyld_info 查找dyld的信息
dyld_usage 可以查找dyld的各项指标
- 在
dyld
将libobjc
库加载后,开始初始化images中全局的构造函数,libobjc.A.dylib
对应的image会调用_objc_init
,注册dyld的map_images
,load_images
事件
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 11.1
* frame #0: 0x00000001007e7734 libobjc.A.dylib`_objc_init at objc-os.mm:926:9
frame #1: 0x00000001005a8553 libdispatch.dylib`_os_object_init + 13
frame #2: 0x00000001005b9d77 libdispatch.dylib`libdispatch_init + 311
frame #3: 0x00007ff823fd2898 libSystem.B.dylib`libSystem_initializer + 238
frame #4: 0x0000000100028ddb dyld`invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const + 182
frame #5: 0x000000010004f257 dyld`invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 242
frame #6: 0x000000010004685a dyld`invocation function for block in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 557
frame #7: 0x0000000100015db3 dyld`dyld3::MachOFile::forEachLoadCommand(Diagnostics&, void (load_command const*, bool&) block_pointer) const + 129
frame #8: 0x00000001000465eb dyld`dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 179
frame #9: 0x000000010004edae dyld`dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 466
frame #10: 0x0000000100028d0e dyld`dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const + 144
frame #11: 0x000000010003c496 dyld`dyld4::APIs::runAllInitializersForMain() + 38
frame #12: 0x000000010001a37d dyld`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3443
frame #13: 0x00000001000194d4 dyld`start + 388
objc_init初始化主要开辟objc相关的内存空间和dyld的load_images
绑定函数
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
// 初始化环境变量Objc的环境变量,设置`OBJC_HELP=1`可以打印所有相关的环境变量
environ_init();
// 绑定线程的key,用于在线程结束时释放key所对应的内存块
tls_init();
// 运行C++的静态构造函数,在dyld调用静态构造函数之前,libc调用`_objc_init`
static_init();
// Runtime运行时初始化,分别是类表和分类表的初始化
runtime_init();
// 初始化libobjc的异常处理系统。
exception_init();
#if __OBJC2__
// 为当前的任务注册一段属于当前任务的可重启的缓存空间范围
cache_t::init();
#endif
// 在mac上才有用,启动回调机制,以及在某些进程加载trampolines.dylib
_imp_implementationWithBlock_init();
// 注册dyld的对image的操作事件执行相应的操作,如加载未载入的分类,加载所有类的`+load`方法
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
- dyld在image加载完成之后触发
objc_init
注册的回调函数,执行objc
相关的设置,+load
和分类+load
,以及方法选择器唯一性修复
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 5.1
* frame #0: 0x00000001043a3f70 ObjectiveCLuanch`+[ViewController load](self=ViewController, _cmd="load") at ViewController.mm:21:5
frame #1: 0x000000010490a517 libobjc.A.dylib`load_images + 1233 //读取objc相关的section
frame #2: 0x00000001045c7de8 dyld_sim`dyld4::RuntimeState::notifyObjCInit(dyld4::Loader const*) + 170 //当动态库加载完成后通过objc的勾子函数
frame #3: 0x00000001045ccab7 dyld_sim`dyld4::Loader::runInitializersBottomUp(dyld4::RuntimeState&, dyld3::Array<dyld4::Loader const*>&) const + 179
frame #4: 0x00000001045ccb60 dyld_sim`dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const + 102
frame #5: 0x00000001045dbb5a dyld_sim`dyld4::APIs::runAllInitializersForMain() + 222 //执行相关的初始化函数为运行程序main函数做准备
frame #6: 0x00000001045bf9fe dyld_sim`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 2528
frame #7: 0x00000001045bfe42 dyld_sim`_dyld_sim_prepare + 379
frame #8: 0x000000010c84db68 dyld`dyld4::prepareSim(dyld4::RuntimeState&, char const*) + 1265
frame #9: 0x000000010c84c6fe dyld`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 244
frame #10: 0x000000010c84c4d4 dyld`start + 388
load_images(const char *path __unused, const struct mach_header *mh)
{
if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
didInitialAttachCategories = true;
loadAllCategories();
}
// 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);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
- 在objc设置完成之后接着执行 C++构造函数,下面是通过
__attribute__((contructor))
定义的构造函数
thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1
* frame #0: 0x00000001043a3f44 ObjectiveCLuanch`permaind() at ViewController.mm:11:5
frame #1: 0x00000001045cc9f7 dyld_sim`invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const + 182
frame #2: 0x00000001045e9291 dyld_sim`invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 242
frame #3: 0x00000001045e258c dyld_sim`invocation function for block in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 555
frame #4: 0x00000001045e16e9 dyld_sim`dyld3::MachOFile::forEachLoadCommand(Diagnostics&, void (load_command const*, bool&) block_pointer) const + 129
frame #5: 0x00000001045e231f dyld_sim`dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 179
frame #6: 0x00000001045e8de8 dyld_sim`dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 466
frame #7: 0x00000001045cc92a dyld_sim`dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const + 144
frame #8: 0x00000001045ccac2 dyld_sim`dyld4::Loader::runInitializersBottomUp(dyld4::RuntimeState&, dyld3::Array<dyld4::Loader const*>&) const + 190
frame #9: 0x00000001045ccb60 dyld_sim`dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const + 102
frame #10: 0x00000001045dbb5a dyld_sim`dyld4::APIs::runAllInitializersForMain() + 222
frame #11: 0x00000001045bf9fe dyld_sim`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 2528
frame #12: 0x00000001045bfe42 dyld_sim`_dyld_sim_prepare + 379
frame #13: 0x000000010c84db68 dyld`dyld4::prepareSim(dyld4::RuntimeState&, char const*) + 1265
frame #14: 0x000000010c84c6fe dyld`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 244
frame #15: 0x000000010c84c4d4 dyld`start + 388
程序入口
在上面的操作完成之后开始进入程序入口的main
函数.这里会调用UIApplicationMain
,创建UIApplication
,并开启一个runLoop循环,同时设置Application
的代理AppDelegate
来代理应用程序的主要声明周期事件.
显示第一帧显示
接下来就是显示视图
loadView
viewDidLoad
layoutSubviews
viewWillAppear
viewDidAppear ---->此时界面对用户可见
AppLaunch
https://devstreaming-cdn.apple.com/videos/wwdc/2019/423lzf3qsjedrzivc7/423/423_optimizing_app_launch.pdf?dl=1
https://devstreaming-cdn.apple.com/videos/wwdc/2022/110362/3/629CBACC-AF8F-4856-98CA-075275ADEAA4/downloads/wwdc2022-110362_sd.mp4?dl=1
https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/KernelProgramming/Architecture/Architecture.html#//apple_ref/doc/uid/TP30000905-CH1g-CACDAEDC
https://opensource.apple.com/tarballs/objc4/
https://opensource.apple.com/tarballs/dyld/
https://github.com/LGCooci/KCCbjc4_debug