iOS APP启动原理
在oc项目中,入口文件是main.m文件,App启动时首先会初始化所有的类,然后再调用main.m中的main函数。
启动过程:
- 从类的初始化到main函数的执行
- 执行AppDelegate中的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
main.m代码
/*
UIApplicationMain
1 创建了 UIApplication 应用对象;
2 创建了Application delegate(AppDelegate)对象
3 建立了一个事件循环 (创建了runloop对象,开启事件循环)
4 读info.plist
创建一个AppDelegate的window
UIWindowLevelAlert > UIWindowLevelStatusBar > UIWindowLevelNormal
*/
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); // 死循环
}
}
虽然main中有return,但其实没有返回任何东西,若return则代表着程序退出。主要原因在于runloop,其内部会有while循环保证程序一直运行。
第一个参数 ——argc,int类型。代表程序启动时的参数个数。默认是1
第二个参数——argv,char[]。代表各个参数的值,默认是程序的名字
第三个参数——principalClassName,NSString类型。主要传UIApplication或其子类的类名对应的字符串。如果nil,就是 UIApplication。(可以自定义UIApplication的子类,处理一些事情如:日志的统计)
第四个参数——delegateClassName,NSString类型。AppDelegate类由编译器自动生成模板。并实现里面的协议以实现我们需要的功能。由于许多配置需要在AppDelegate中进行设置,因此会造成文件代码的臃肿,可以创建APPDelegate的分类,在分类中配置一些第三方库需要的配置参数,或者广告页等,以达到瘦身的目的。
在xcode5之前,通常会写这样一段代码
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
ViewController * rootVC = [ViewController new];
self.window.rootViewController = rootVC;
[self.window makeKeyAndVisible];
return YES;
}
有了storyboard之后就不需要了。为什么不需要了呢?APPDelegate是如何与ViewController建立联系的。主要还是回到main函数中的UIApplicationMain这个类,在初始化的时候会加载info.plist这个资源文件,如下图所示
在资源文件中Main storyboard file base name 指定了加载Main.StoryBoard作为应用的入口文件,而Main.StoryBoard又与ViewContoller进行了关联,所以不需要做其它操作。
如果我们把这个配置给删除掉,那么则需要在AppDelegate的协议中写上上面那段代码。
在swift项目中其实没有main文件。而在APPDelegate.swift中多一个注解@UIApplicationMain,系统会默认将该文件作为UIApplicationMain的第四个参数。如果想要自定义,需要注释掉@UIApplicationMain,然后创建main.swift
import UIKit
import Foundation
UIApplicationMain(CommandLine.argc,
UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory(to: UnsafeMutablePointer<Int8>.self, capacity: Int(CommandLine.argc)),
nil,
NSStringFromClass(AppDelegate.self)
)
这样就OK了。如果不使用自定义的UIApplication,那么创建main.swift文件的意义就不大。
自定义UIApplication
UIApplicationMain的第三个参数是UIApplication对象。如果传nil,则会默认调用UIApplication对象。如果我们自定义UIApplication可以提完成我们自定义的。在定义的UIApplication对象我们可以统一处理一些业务,比如日志等
import UIKit
class CustomApplication: UIApplication {
//uiapplicton中有与Event相关的api,所以可以利用这些api完成记录行为日志的任务
override func sendEvent(_ event: UIEvent) {
//在这里处理一些统一的逻辑
print("来自UIApplication 的 event")
return super .sendEvent(event)
}
override func sendAction(_ action: Selector, to target: Any?, from sender: Any?, for event: UIEvent?) -> Bool {
//在这里处理一些统一的逻辑
//例如 longinController的,某个action
if target is ViewController && sender is UIButton && NSStringFromSelector(action) == "loginAction"{
//记录日志或者上传服务器
print("来自UIApplication 的 sendAction")
}
return super.sendAction(action, to: target, from: sender, for: event)
}
}
在ViewController中添加一个button,并点击按钮
UIApplication的sendEvent被调用多次,然后sendAction方法被处罚,最后执行button绑定的事件