App启动时间,直接影响用户对app的第一体验和判断.如果启动时间过长,不单用户体验会下降, 还有可能会触发苹果的watch dog机制而kill掉App, 所以App启动时间优化也十分重要
App启动流程
App的启动如下图所示:
image
那么App的启动时间大致可分为两部分, 第一部分为main函数执行之前
的加载时间主要是系统的动态链接库和可执行文件的加载时间;第二部分是main函数开始到 application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
执行结束前的时间
main函数以前消耗的时间(pre-main)
首先需要在scheme中添加配置, 如下图
image然后启动App就能在控制台看到如下信息, 时间消耗的项目十分明显, 如下所示:
Total pre-main time: 2.6 seconds (100.0%)
dylib loading time: 1.8 seconds (70.4%)
rebase/binding time: 535.42 milliseconds (20.2%)
ObjC setup time: 47.95 milliseconds (1.8%)
initializer time: 200.64 milliseconds (7.5%)
slowest intializers :
AFNetworking : 158.25 milliseconds (5.9%)
main函数有花费的时间
面函数以后花费的时间主要是从main函数开始到 application:didFinishLaunchingWithOptions:
之间的时间, 方法如下:
main函数中记录时间的起点
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
CFAbsoluteTime appStartLaunchTime;
int main(int argc, char * argv[]) {
@autoreleasepool {
appStartLaunchTime = CFAbsoluteTimeGetCurrent();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
application: didFinishLaunchingWithOptions:
中记录时间的终点, 取两者之差即可
extern CFAbsoluteTime appStartLaunchTime;
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"app启动时间%f", CFAbsoluteTimeGetCurrent() - appStartLaunchTime);
});
//....
return YES;
}
@end
如何减少main()调用之前的耗
-
减少不必要的framework,特别是第三方的,因为动态链接比较耗时
-
check framework应设为optional和required,如果该framework在当前App支持的所有iOS系统版本都存在,那么就设为required,否则就设为optional,因为optional会有些额外的检查;
-
合并或者删减一些OC类
-
删减一些无用的静态变量
-
删减没有被调用到或者已经废弃的方法
-
将不必须在+load方法中做的事情延迟到+initialize中
Swift如何计算启动时间
Swift项目是没有main文件,官方给了如下解释:
@UIApplicationMain to a regular Swift file. This causes the compiler to synthesize a mainentry point for your iOS app, and eliminates the need for a “main.swift” file.
也就是说,通过添加@UIApplicationMain
标志的方式,帮我们添加了mian函数了。所以如果是我们需要在mian函数中做一些其它操作的话,需要我们自己来创建main.swift文件,这个也是苹果允许的。
-
注释掉AppDelegate类中的 @UIApplicationMain标志;
-
自行创建
main.swift
(<mark>注意:main一定要小写, 不能写成Main否则报错</mark>)文件,并添加程序入口, 代码如下:import UIKit var appStartLaunchTime: CFAbsoluteTime = CFAbsoluteTimeGetCurrent() UIApplicationMain(CommandLine.argc, UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory(to: UnsafeMutablePointer<Int8>.self,capacity: Int(CommandLine.argc)), nil, NSStringFromClass(AppDelegate.self))
-
然后在AppDelegate中编写如下代码:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. // APP启动时间耗时,从mian函数开始到didFinishLaunchingWithOptions方法结束 DispatchQueue.main.async { print("APP启动时间耗时,从mian函数开始到didFinishLaunchingWithOptions方法:\(CFAbsoluteTimeGetCurrent() - appStartLaunchTime)。") } return true }
网友评论