wpf 加载page后启动_iOSApp启动过程

655b2366f489d541eab4c457278c3d9b.png

启动流程如图

9a96b38dc65a564b537017e06a1aafea.png


总结来说,大体分为如下步骤:
(1) 系统为程序启动做好准备
(2) 系统将控制权交给 Dyld,Dyld 会负责后续的工作
(3) Dyld 加载程序所需的动态库
(3) Dyld 对程序进行 rebase 以及 bind 操作
(4) Objc SetUp
(5) 运行初始化函数
(6) 执行程序的 main 函数

eb0fe88c7534d1d94cec53797e6341ca.png


加载动态库
dyld会首先读取mach-o文件的Header和load commands。 接着就知道了这个可执行文件依赖的动态库。

  • dyld3

3a7fd1e9b1e871cd5a8bac563216e257.png


dyld3是部分out-of-process,部分in-process。图中,虚线之上的部分是out-of-process的,在App下载安装和版本更新的时候会去执行,out-of-process会做如下事情:
Rebase 和 Bind
有两种主要的技术来保证应用的安全:ASLR和Code Sign。

  • ASLR ASLR的全称是Address space layout randomization。App启动的时候,程序会被影射到逻辑的地址空间,这个逻辑的地址空间有一个起始地址,而ASLR技术使得这个起始地址是随机的。如果是固定的,那么黑客很容易就可以由起始地址+偏移量找到函数的地址。
  • Code Sign Code Sign相信大多数开发者都知晓,这里要提一点的是,在进行Code sign的时候,加密哈希不是针对于整个文件,而是针对于每一个Page的。这就保证了在dyld进行加载的时候,可以对每一个page进行独立的验证。

mach-o中有很多符号,有指向当前mach-o的,也有指向其他dylib的,比如printf。那么,在运行时,代码如何准确的找到printf的地址呢?
mach-o中采用了PIC技术,全称是Position Independ code。当你的程序要调用printf的时候,会先在__DATA段中建立一个指针指向printf,在通过这个指针实现间接调用。dyld这时候需要做一些fix-up工作,即帮助应用程序找到这些符号的实际地址。主要包括两部分

  • Rebase 修正内部(指向当前mach-o文件)的指针指向
  • Bind 修正外部指针指向

1249ed938ff402b6da03fc1a96721df6.png


之所以需要Rebase,是因为刚刚提到的ASLR使得地址随机化,导致起始地址不固定,另外由于Code Sign,导致不能直接修改Image。Rebase的时候只需要增加对应的偏移量即可。待Rebase的数据都存放在__LINKEDIT中。
可以通过MachOView查看:Dynamic Loader Info -> Rebase Info

 192:Desktop Leo$ xcrun dyldinfo -bind demo  bind information: segment section          address        type    addend dylib            symbol __DATA  __got            0x10003C038    pointer      0 PullToRefreshKit __T016PullToRefreshKit07DefaultC4LeftC9textLabelSo7UILabelCvWvd __DATA  __got            0x10003C040    pointer      0 PullToRefreshKit __T016PullToRefreshKit07DefaultC5RightC9textLabelSo7UILabelCvWvd __DATA  __got            0x10003C048    pointer      0 PullToRefreshKit __T016PullToRefreshKit07DefaultC6FooterC9textLabelSo7UILabelCvWvd __DATA  __got            0x10003C050    pointer      0 PullToRefreshKit __T016PullToRefreshKit07DefaultC6HeaderC7spinnerSo23UIActivityIndicatorViewCvWvd //... 复制代码


Rebase解决了内部的符号引用问题,而外部的符号引用则是由Bind解决。在解决Bind的时候,是根据字符串匹配的方式查找符号表,所以这个过程相对于Rebase来说是略慢的。
同样,也可以通过xcrun dyldinfo来查看Bind的信息,比如我们查看bind信息中,包含UITableView的部分:

 192:Desktop Leo$ xcrun dyldinfo -bind demo | grep UITableView __DATA  __objc_classrefs 0x100041940    pointer      0 UIKit            _OBJC_CLASS_$_UITableView __DATA  __objc_classrefs 0x1000418B0    pointer      0 UIKit            _OBJC_CLASS_$_UITableViewCell __DATA  __objc_data      0x100041AC0    pointer      0 UIKit            _OBJC_CLASS_$_UITableViewController __DATA  __objc_data      0x100041BE8    pointer      0 UIKit            _OBJC_CLASS_$_UITableViewController __DATA  __objc_data      0x100042348    pointer      0 UIKit            _OBJC_CLASS_$_UITableViewController __DATA  __objc_data      0x100042718    pointer      0 UIKit            _OBJC_CLASS_$_UITableViewController __DATA  __data           0x100042998    pointer      0 UIKit            _OBJC_METACLASS_$_UITableViewController __DATA  __data           0x100042A28    pointer      0 UIKit            _OBJC_METACLASS_$_UITableViewController __DATA  __data           0x100042F10    pointer      0 UIKit            _OBJC_METACLASS_$_UITableViewController __DATA  __data           0x1000431A8    pointer      0 UIKit            _OBJC_METACLASS_$_UITableViewController 复制代码


Objc
C++ 会为静态创建的对象生成初始化器,与静态语言不同,OC基于Runtime机制可以用类的名字来实例化一个类的对象。Runtime 维护了一张映射类名与类的全局表,当加载一个 dylib 时,其定义的所有的类都需要被注册到这个全局表中。ObjC 在加载时可以通过 fix-up 在动态类中改变实例变量的偏移量,利用这个技术可以在不改变dylib的情况下添加另一个 dylib 中类的方法,而非常见的通过定义类别(Category)的方式改变一个类的方法。
Dyld 在 bind 操作结束之后,会发出 dyld_image_state_bound 通知,然后与之绑定的回调函数 map_2_images 就会被调用,它主要做以下几件事来完成 Objc Setup:
Initializers
Objc SetUp 结束后,Dyld 便开始运行程序的初始化函数,该任务由 initializeMainExecutable 函数执行。整个初始化过程是一个递归的过程,顺序是先将依赖的动态库初始化,然后在对自己初始化。初始化需要做的事情包括:

  • 调用 Objc 类的 + load 函数
  • 调用 C++ 中带有 constructor 标记的函数
  • 非基本类型的 C++ 静态全局变量的创建

所谓执行监控启动crash的思路都是在这里构建的。下面是一些方法的执行顺序,initialize的顺序可能在更早,但总是会在load和launch之间。
程序启动逻辑
最后 dyld 会调用 main() 函数。main() 会调用 UIApplicationMain(),程序启动。
main.m文件,此处就是应用的入口了。程序启动时,先执行main函数,main函数是ios程序的入口点,内部会调用UIApplicationMain函数,UIApplicationMain里会创建一个UIApplication对象 ,然后创建UIApplication的delegate对象 —–(您的)AppDelegate ,开启一个消息循环(main runloop),每当监听到对应的系统事件时,就会通知AppDelegate。

 int main(int argc, char * argv[]) {     @autoreleasepool {         return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));     } } 复制代码
  

31a9d2ee7852cfe8e9f8d11609ff8286.png


iOS开发交流技术群:563513413,不管你是大牛还是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值