页面加载时间指的页面从创建到可见的时间.
严格意义上来说页面加载时间测试,更应该是页面的冷加载,不包含接口返回数据时间.
页面加载时间能反应代码中创建页面视图是否有过度绘制或者绘制不合理导致创建视图时间过长的情况.
UIViewController 是画面控制的中心类,包含导航条、标签条、工具条等多种功能界面,主要功能是用于控制画面的切换,其中的 view 属性管理整个画面的外观。
viewDidLoad: 载入完成,可以进行自定义数据以及动态创建其他控件
viewWillAppear: 视图即将出现在屏幕之前
viewDidAppear: 视图已经在屏幕上渲染完成
viewWillDisappear: 视图即将从屏幕上移除
viewDidDisappear: 视图已经被从屏幕上移除
dealloc: 视图被销毁
一般项目代码都会继承UIViewController做一些封装,然后其他页面继承这个view基类.
页面开始创建点
- (void)viewDidLoad {
[super viewDidLoad];
//继承了UIViewController的viewDidLoad方法
self.statusBarStyle = UIStatusBarStyleDefault;
[self.view setBackgroundColor:[UIColor whiteColor]];
NSLog(@"page-test-start:%@",NSStringFromClass([self class]));
self.StartTime = [[NSDate date] timeIntervalSince1970];
}
页面展示结束点
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
//继承了UIViewController的viewDidAppear方法
NSLog(@"page-test-end:%@",NSStringFromClass([self class]));
// 用NSStringFromClass方法获取当前的页名称
self.EndTime = [[NSDate date] timeIntervalSince1970];
CGFloat rounded_up = round((self.EndTime-self.StartTime) * 1000);
NSLog(@"page-test-total:%.2lf",rounded_up);
}
操作app并且使用idevicesyslog过滤关键字:
idevicesyslog | grep -e page-test-end -e page-test-total
output:
Sep 30 16:33:06 xinxide-iPhone xxxxx[2195] <Notice>: page-test-end:HomeViewController
Sep 30 16:33:06 xinxide-iPhone xxxxx[2195] <Notice>: page-test-total:379.00
Sep 30 16:33:09 xinxide-iPhone xxxxx[2195] <Notice>: page-test-end:UserInfoViewControllerV2
Sep 30 16:33:09 xinxide-iPhone xxxxx[2195] <Notice>: page-test-total:239.00
Sep 30 16:33:12 xinxide-iPhone xxxxx[2195] <Notice>: page-test-end:LoginRegisterViewControllerV2
Sep 30 16:33:12 xinxide-iPhone xxxxx[2195] <Notice>: page-test-total:631.00
Sep 30 16:33:14 xinxide-iPhone xxxxx[2195] <Notice>: page-test-end:LoginViewControllerV2
Sep 30 16:33:14 xinxide-iPhone xxxxx[2195] <Notice>: page-test-total:567.00
第一种方法在你需要知道 view 的基类叫什么名字并且在代码中打点,这样做每次都有麻烦.
所以想使用拦截 viewDidLoad 和 viewDidAppear 这两个函数,就拦截器中打印时间就可以了.
Aspects 库是一个是 iOS 上的轻量级 AOP 库,https://github.com/steipete/Aspects, 另外 Aspects 封装了 iOS runtime 的特性
简单来说,AOP(Aspect Oriented Programming) 是面向切面编程,主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。
参考:https://www.jianshu.com/p/addd4eac54ed
使它能够将自身的代码「融入」被勾住(Hook)的程序的进程中,成为目标进程的一个部分。API Hook 技术是一种用于改变 API 执行结果的技术,能够将系统的 API 函数执行重定向。
在podfile引用如下:
target "UICatalog" do
xcodeproj 'UICatalog.xcodeproj'
pod 'YYKit'
pod 'Aspects'
end
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[getperformance new] performancethread];//获取性能数据
/**
* 事件拦截
* 拦截UIViewController的viewDidLoad方法
*/
[UIViewController aspect_hookSelector:@selector(viewDidLoad) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo){
NSLog(@"%@ 对象的viewDidLoad调用了",aspectInfo.instance);
self.StartTime = [[NSDate date] timeIntervalSince1970];
/**
* 添加我们要执行的代码,由于withOptions是AspectPositionAfter。
* 所以每个控制器的viewDidLoad触发都会执行下面的方法
*/
} error:NULL];
/**
* 事件拦截
* 拦截UIViewController的viewDidAppear方法
*/
[UIViewController aspect_hookSelector:@selector(viewDidAppear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo){
NSLog(@"%@ 对象的viewDidAppear调用了",aspectInfo.instance);
self.EndTime = [[NSDate date] timeIntervalSince1970];
CGFloat rounded_up = round((self.EndTime-self.StartTime) * 1000);
NSLog(@"%@页面,页面加载耗时:%.2lf",aspectInfo.instance,rounded_up);
/**
* 添加我们要执行的代码,由于withOptions是AspectPositionAfter。
* 所以每个控制器的viewDidLoad触发都会执行下面的方法
*/
} error:NULL];
return YES;
}
output:
2018-10-02 18:11:44.361065+0800 UICatalog[22872:5780236] <AAPLAlertViewController: 0x7ffd0cf2c870>页面,页面加载耗时:518.00
2018-10-02 18:11:50.289907+0800 UICatalog[22872:5780236] <AAPLPageControlViewController: 0x7ffd0ce39080>页面,页面加载耗时:523.00
2018-10-02 18:11:52.131938+0800 UICatalog[22872:5780236] <AAPLDatePickerController: 0x7ffd0ce3a550>页面,页面加载耗时:631.00
代码地址: https://github.com/xinxi1990/iOSPerformanceTest.git
app 专项测试已经做了大半年了,从无到有,从有到持续优化.
对我而言有两点思考.
1.专项测试测出来的数据结果,其实并不是记录一个数值而已,更需求了解其背后的技术特性.
2.测试结果是否能提供给开发同学优化的价值.