app的启动

前言

本篇文章讲解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
  • 显示界面,完成。

你可能感兴趣的:(程序设计-Object-C,cocoa,macos,objective-c)