iOS 程序加载流程分析

简介: dyld简介;dyld共享缓存;dyld程序启动过程;_objc_init符号断点:

总纲领: OC底层探寻


先通过一段代码抛出疑问:


//ViewController.m文件 只是在.m文件中, 放出了load方法  
import "ViewController.h"  
@interface ViewController ()  
@end  
@implementation ViewController  
+ (void)load{  
    NSLog(@"%s",__func__);   
}  
- (void)viewDidLoad {  
    [super viewDidLoad];  
    // Do any additional setup after loading the view.  
}  
//在工程中的mian.m文件中, 写如下代码, 
// load -> Cxx -> main  
__attribute__((constructor)) void kcFunc(){  
    printf("来了 : %s \n",__func__);  
}  
// 内存 main() dyld image init 注册回调通知 - dyld_start  -> dyld::main()  -> main()  
// rax  
int main(int argc, char * argv[]) {  
    NSString * appDelegateClassName;  
    NSLog(@"1223333");  
    @autoreleasepool {  
        // Setup code that might create autoreleased objects goes here.  
        appDelegateClassName = NSStringFromClass([AppDelegate class]);  
    }  
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);  
}


ViewController .m文件中的load方法, 以及在main.m文件中的c**函数方法以及main函数方法的打印顺序, 通过打印结果我们可以看到打印的顺序如下:


//打印结果  
2020-09-27 15:46:32.959435+0800 002-应用程加载分析[22871:3847093] +[ViewController load]  
来了 : kcFunc   
2020-09-27 15:46:32.959926+0800 002-应用程加载分析[22871:3847093] 1223333


load方法  -> C**方法 -> main函数 为什么执行顺序会是这样的?

我们在每个方法中加断点并打印堆栈, 结果如下:


//在load函数中的断点 bt
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * frame #0: 0x000000010970be17 002-应用程加载分析`+[ViewController load](self=ViewController, _cmd="load") at ViewController.m:17:5
    frame #1: 0x00007fff201805e3 libobjc.A.dylib`load_images + 1442
    frame #2: 0x000000010971fe54 dyld_sim`dyld::notifySingle(dyld_image_states, ImageLoader const*, ImageLoader::InitializerTimingList*) + 425
    frame #3: 0x000000010972e887 dyld_sim`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 437
    frame #4: 0x000000010972cbb0 dyld_sim`ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 188
    frame #5: 0x000000010972cc50 dyld_sim`ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 82
    frame #6: 0x00000001097202a9 dyld_sim`dyld::initializeMainExecutable() + 199
    frame #7: 0x0000000109724d50 dyld_sim`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 4431
    frame #8: 0x000000010971f1c7 dyld_sim`start_sim + 122
    frame #9: 0x0000000117efa85c dyld`dyld::useSimulatorDyld(int, macho_header const*, char const*, int, char const**, char const**, char const**, unsigned long*, unsigned long*) + 2308
    frame #10: 0x0000000117ef84f4 dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 837
    frame #11: 0x0000000117ef3227 dyld`dyldbootstrap::start(dyld3::MachOLoaded const*, int, char const**, dyld3::MachOLoaded const*, unsigned long*) + 453
    frame #12: 0x0000000117ef3025 dyld`_dyld_start + 37
    //在C**函数中的断点 bt
    (lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
  * frame #0: 0x0000000110000084 002-应用程加载分析`kcFunc at main.m:13:5
    frame #1: 0x000000011002794b dyld_sim`ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) + 537
    frame #2: 0x0000000110027d34 dyld_sim`ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&) + 40
    frame #3: 0x0000000110022899 dyld_sim`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 455
    frame #4: 0x0000000110020bb0 dyld_sim`ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 188
    frame #5: 0x0000000110020c50 dyld_sim`ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 82
    frame #6: 0x00000001100142a9 dyld_sim`dyld::initializeMainExecutable() + 199
    frame #7: 0x0000000110018d50 dyld_sim`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 4431
    frame #8: 0x00000001100131c7 dyld_sim`start_sim + 122
    frame #9: 0x00000001165a985c dyld`dyld::useSimulatorDyld(int, macho_header const*, char const*, int, char const**, char const**, char const**, unsigned long*, unsigned long*) + 2308
    frame #10: 0x00000001165a74f4 dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 837
    frame #11: 0x00000001165a2227 dyld`dyldbootstrap::start(dyld3::MachOLoaded const*, int, char const**, dyld3::MachOLoaded const*, unsigned long*) + 453
    frame #12: 0x00000001165a2025 dyld`_dyld_start + 37


我们可以通过分析dyld的源码来分析程序的加载流程, 用一副图可以总结如下:


微信图片_20220509111615.png

dyld流程分析图.png


我们可以看到在程序的加载过程中, dyld起到了至关重要的作用.


dyld简介


dyld(the dynamic link editor)是苹果的动态链接器,是苹果操作系统一个重要组成部分,在系统内核做好程序准备工作之后,交由dyld负责余下的工作。而且它是开源的,任何人可以通过苹果官网下载它的源码来阅读理解它的运作方式,了解系统加载动态库的细节。dyld下载地址


在iOS系统中,几乎所有的程序都会用到动态库,而动态库在加载的时候都需要用dyld(位于/usr/lib/dyld)程序进行链接。很多系统库几乎都是每个程序都要用到的,与其在每个程序运行的时候一个一个将这些动态库都加载进来,还不如先把它们打包好,一次加载进来来的快。


dyld共享缓存


dyld加载时,为了优化程序启动,启用了共享缓存(shared cache)技术。共享缓存会在进程启动时被dyld映射到内存中,之后,当任何Mach-O映像加载时,dyld首先会检查该Mach-O映像与所需的动态库是否在共享缓存中,如果存在,则直接将它在共享内存中的内存地址映射到进程的内存地址空间。在程序依赖的系统动态库很多的情况下,这种做法对程序启动性能是有明显提升的。


dyld程序启动过程


系统先读取App的可执行文件(Mach-O文件),从里面获得dyld的路径,然后加载dyld,dyld去初始化运行环境,开启缓存策略,加载程序相关依赖库(其中也包含我们的可执行文件),并对这些库进行链接,最后调用每个依赖库的初始化方法,在这一步,runtime被初始化。当所有依赖库的初始化后,轮到最后一位(程序可执行文件)进行初始化,在这时runtime会对项目中所有类进行类结构初始化,然后调用所有的load方法。最后dyld返回main函数地址,main函数被调用,我们便来到了熟悉的程序入口。


  • ImageLoader : 用于辅助加载特定可执行文件格式的类,程序中对应实例可简称为image(如程序可执行文件,Framework库,bundle文件)。


_objc_init符号断点:


微信图片_20220509111626.png

1419656-0cb5ee83eefa4ac6.png


这里可以看到_objc_init调用的顺序,先libSystem_initializer调用libdispatch_init再到_objc_init初始化runtime。


runtime初始化后不会闲着,在_objc_init中注册了几个通知,从dyld这里接手了几个活,其中包括负责初始化相应依赖库里的类结构,调用依赖库里所有的load方法。


就拿sMainExcuatable来说,它的initializer方法是最后调用的,当initializer方法被调用前dyld会通知runtime进行类结构初始化,然后再通知调用load方法,这些目前还发生在main函数前,但由于lazy bind机制,依赖库多数都是在使用时才进行bind,所以这些依赖库的类结构初始化都是发生在程序里第一次使用到该依赖库时才进行的。

文章参考: 【IOS开发高级系列】dyld专题




目录
相关文章
|
2月前
|
搜索推荐 Android开发 iOS开发
安卓与iOS系统的用户界面设计对比分析
本文通过对安卓和iOS两大操作系统的用户界面设计进行对比分析,探讨它们在设计理念、交互方式、视觉风格等方面的差异及各自特点,旨在帮助读者更好地理解和评估不同系统的用户体验。
66 1
|
2月前
|
API 数据安全/隐私保护 iOS开发
利用uni-app 开发的iOS app 发布到App Store全流程
利用uni-app 开发的iOS app 发布到App Store全流程
164 3
|
2月前
|
存储 运维 安全
iOS加固原理与常见措施:保护移动应用程序安全的利器
iOS加固原理与常见措施:保护移动应用程序安全的利器
55 0
|
11天前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
【7月更文挑战第4天】在移动应用开发的广阔天地中,安卓和iOS两大平台各据一方,引领着技术潮流。本文将深入探讨这两个平台的开发环境,从编程语言、工具链到市场分布等多个维度进行比较。我们将揭示各自的优势与局限,并分析开发者如何在这两个不同的生态系统中做出选择。通过本文,读者将获得一个全面的视角,理解两大平台在开发实践中的差异性及其对项目成功的影响。
|
1月前
|
Java Android开发 iOS开发
深入探讨移动操作系统的性能优化:安卓与iOS的对比分析
在现代移动设备中,操作系统的性能优化至关重要。本文从系统架构、内存管理、电池续航和应用程序运行效率等多个维度,深入探讨了安卓(Android)和iOS两大主流移动操作系统的优化策略及其实际效果,旨在为开发者和用户提供更清晰的了解和选择依据。
42 0
|
2天前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文深入探讨了两者的开发环境差异,从编程语言、工具框架到用户群体和市场份额进行了全面比较。通过数据支撑和案例分析,揭示了不同平台的优势与局限,旨在为开发者提供决策参考,同时预测未来发展趋势。
|
4天前
|
API 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动操作系统的两大阵营中,安卓和iOS各占据着重要的地位。本文旨在深入探讨两种系统在开发环境方面的异同,通过对开发工具、语言、框架、API以及生态系统的综合比较,揭示各自在应用开发上的优势与挑战。文章将基于最新的市场数据和技术发展进行论述,以期为开发者提供有价值的参考信息,并预测未来可能的发展趋势。
10 1
|
20天前
|
API 开发工具 Android开发
安卓与iOS开发环境对比分析
本文将探讨安卓与iOS两大移动操作系统在开发环境中的差异,包括编程语言、工具链、API设计哲学、应用商店政策以及开发者社区支持等方面。通过比较分析,旨在帮助开发者更好地选择适合自己项目需求的平台,并理解不同环境下的开发挑战和机遇。
|
6天前
|
搜索推荐 Android开发 iOS开发
探索Android与iOS开发的差异:平台特性与用户体验的对比分析
【7月更文挑战第9天】在移动应用开发的浩瀚海洋中,Android和iOS两大操作系统如同两座灯塔,指引着开发者们的航向。本文将深入探讨这两个平台在开发环境、用户界面设计、性能优化以及市场策略上的根本差异。我们将通过比较分析,揭示各自平台的独特优势和潜在挑战,为开发者提供决策支持,同时也为用户体验的提升指明方向。
|
1月前
|
安全 Android开发 iOS开发
探索Android与iOS开发的差异:平台特性与用户体验的对比分析
在移动应用开发的广阔天地中,Android和iOS两大阵营各据一方。本文将深入探讨这两个操作系统在开发环境、编程语言、用户界面设计及市场分布等方面的主要区别。通过比较分析,我们将揭示各自平台的特有优势,并讨论如何根据目标受众和业务需求选择适合的开发平台。