前言
本篇文章讲解ios的应用程序的启动
应用程序的加载
点击一个app
首先,我们在手机上点击一个app图标
内核初始化
- 操作系统收到启动app的消息后,会调用内核代码初始化内存空间,为app创建进程
- 然后操作系统通过系统调用读取并解析app的可执行文件
- 然后操作系统的动态链接器根据app的可执行文件的符号表去加载app运行依赖的动态库,或者叫共享库,并与符号表进行绑定,如果动态库已经加载过的话就直接绑定就行了,因为很多动态库是app共享的,可能在内存常驻。
- 经过动态绑定以后,app的所有可执行代码和数据都已经准备就绪了,这个时候操作系统为app分配堆栈空间,拷贝代码和数据到内存等一系列操作
- 然后确定程序启动的内存位置,开始执行具体的二进制指令
从开始到main函数
代码指令并不是从main函数开始执行的,在main函数之前还有一系类的关于代码的初始化操作,比如:
- runtime运行时初始化。
- 全局变量的初始化
main
int main(int argc, char * argv[]) {NSString * appDelegateClassName;@autoreleasepool {// Setup code that might create autoreleased objects goes here.appDelegateClassName = NSStringFromClass([AppDelegate class]);}return UIApplicationMain(argc, argv, nil, appDelegateClassName);}
上面代码是一个main函数的执行代码,一般我们不需要修改这个函数中的代码,main函数主要的操作就是执行函数UIApplicationMain,该函数的原型如下:
// argc、argv:直接传递给UIApplicationMain进行相关处理即可// principalClassName:指定应用程序类名(app的象征)// 该类必须是UIApplication(或子类)。// 如果为nil,则用UIApplication类作为默认值// delegateClassName:指定应用程序的代理类,该类必须遵守UIApplicationDelegate协议int UIApplicationMain(int argc, char * _Nullable argv[_Nonnull], NSString * _Nullable principalClassName, NSString * _Nullable delegateClassName);
- 这也是第一步,执行UIApplicationMain
- 第二步在UIApplicationMain内部执行,根据principalClassName创建UIApplication对象
- 第三步根据delegateClassName创建一个delegate对象,并将该delegate对象赋值给UIApplication对象中的delegate属性。所以,我们的AppDelegate遵循UIApplicationDelegate协议
@interface AppDelegate : UIResponder
- 开启一个主运行循环,处理事件,可以让程序保持运行
- 接下来,要加载info.plist,info.plist相当于我们应用程序的配置文件,在这里我们需要分开说明一下接下来的步骤啦,因为
苹果公司在iOS 13的时候引入了多窗口的概念
。
IOS 13之前
- 如果我们在info.plist设置了
Launch screen interface file base name
,程序会先加载Launch screen界面,这是个启动界面 - 如果我们在info.plist设置了
Main storyboard file base name
,程序会加载Main storyboard界面,该文件中保存了我们app的Root View Controller,否则直接跳到下一步 - 调用代理的application:didFinishLaunchingWithOptions:方法,一般情况下,如果我们没有创建主界面的话,会在这一步创建主界面,如果我们设置了
Main storyboard file base name
,然后又在该函数内创建了一个新界面,新界面会覆盖之前的界面。- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
在IOS 13之前,应用程序的状态监听都是通过UIApplicationDelegate协议实现的,下面列出常用的UIApplicationDelegate协议。
UIApplicationDelegate协议
// 该方法是程序启动,但还没进入状态保存时执行。// 该方法在启动界面之后执行- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions API_AVAILABLE(ios(6.0));// 该方法是程序启动基本完成,准备开始运行时执行。// 该方法在启动界面之后执行,一般需要在该方法内创建主界面- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions API_AVAILABLE(ios(3.0));// 该方法是当程序将要进入非活动状态时执行,在此期间,应用程序不接收消息或事件,比如按下Home键或者来电话了。- (void)applicationWillResignActive:(UIApplication *)application// 该方法是当程序已经进入后台时执行- (void)applicationDidEnterBackground:(UIApplication *)application// 该方法是当程序将要进入前台时执行- (void)applicationWillEnterForeground:(UIApplication *)application// 该方法是当程序进入活动状态时执行- (void)applicationDidBecomeActive:(UIApplication *)application// 该方法是当程序程序将要退出时执行- (void)applicationWillTerminate:(UIApplication *)application
IOS 13之后
如果我们设置ios程序的Mimimum Deployments的iOS版本小于13,程序依旧采用上述的UIApplicationDelegate协议,但是如果我们的iOS版本大于13,上述UIApplicationDelegate协议的
- applicationWillResignActive
- applicationDidEnterBackground
- applicationWillEnterForeground
- applicationDidBecomeActive
都不会执行,因为这几种状态被认为是scene的状态改变而不是应用程序的状态改变了。事实上,info.plist的加载都需要改变。
为了实现多窗口功能,苹果修改了使用多年的AppDelegate,把AppDelegate分为了两部分,AppDelegate和SceneDelegate,其中
- AppDelegate处理应用程序状态的改变,比如应用程序的加载,退出
- AppDelegate中新增对scene的支持
// 新建场景时会调用,返回场景配置信息// 我们一般在这个里面记载Scene Configuration// 下面是一个例子:return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options// 关闭场景时调用- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions
- SceneDelegate处理场景的生命周期
// 配置UIWindow“窗口”并将其附加到所提供的UIWindowScene“场景”// 如果在配置文件中配置了storyboard。UIWindow“窗口”将自动初始化并附加到场景- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions// 在系统释放场景时或者场景进入后台调用- (void)sceneDidDisconnect:(UIScene *)scene// 当场景从非活动状态移动到活动状态时调用。// 使用此方法可以重新启动场景处于非活动状态时暂停(或尚未启动)的任何任务。- (void)sceneDidBecomeActive:(UIScene *)scene// 当场景将从活动状态移动到非活动状态时调用。// 比如来电话或切入后台- (void)sceneWillResignActive:(UIScene *)scene// 在场景从背景过渡到前景时调用。- (void)sceneWillEnterForeground:(UIScene *)scene// 场景进入后台后调用- (void)sceneDidEnterBackground:(UIScene *)scene
程序支持的场景需要在Info.plist中声明,由Info.plist->Application Scene Manifest->Scene Configuration->Application Session Role节点指定场景List,每一项包含以下节点:
- Configuration Name:场景配置的唯一标识;
- Delegate Class Name:实现UIWindowSceneDelegate代理类的名称;
- Storyboard Name:Storyboard的名称(如果采用的是Storyboard方式实现UI),可选。
使用Scene Delegate之后,UIApplicationDelegate将不再持有UIWindow,它将转移至UIWindowSceneDelegate代理中
最后我们从加载配置文件开始重新梳理一下对于大于IOS13的启动过程
- 首先调用AppDelegate中的
configurationForConnectingSceneSession协议
,该协议会返回默认加载场景的UISceneConfiguration* - 在
Info.plist->Application Scene Manifest->Scene Configuration->Application Session Role
中寻找场景列表,如果有,根据上一步返回的值匹配应该加载哪个场景,如果上一步返回nil,匹配列表中的第一个。如果列表为空,不加载场景,这时候应该是黑屏 - 如果匹配到了场景信息
- 如果包含storyboard,加载界面storyboard到UIWindow,执行下一步
- 否则,继续执行下一步
- 开始执行场景委托的willConnectToSession协议方法
- 该协议中可以初始化并显示UIwinodw,如果不进行处理,UIWindow就用之前加载过的storyboard
- 显示界面,完成。