服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - IOS - iOS性能优化教程之页面加载速率详解

iOS性能优化教程之页面加载速率详解

2021-05-10 13:52菠萝ngmm IOS

在软件开发领域里经常能听到这样一句话,“过早的优化是万恶之源”,不要过早优化或者过度优化。下面这篇文章主要给大家介绍了关于iOS性能优化教程之页面加载速率的相关资料,需要的朋友可以参考下

前言

我认为在编码过程中时刻注意性能影响是有必要的,但凡事都有个度,不能为了性能耽误了开发进度。在时间紧急的情况下我们往往采用“quick and dirty”的方案来快速出成果,后面再迭代优化,即所谓的敏捷开发。与之相对应的是传统软件开发中的瀑布流开发流程。

卡顿产生的原因

iOS性能优化教程之页面加载速率详解

在 ios 系统中,图像内容展示到屏幕的过程需要 cpu 和 gpu 共同参与。cpu 负责计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后 cpu 会将计算好的内容提交到 gpu 去,由 gpu 进行变换、合成、渲染。之后 gpu 会把渲染结果提交到帧缓冲区去,等待下一次 vsync 信号到来时显示到屏幕上。由于垂直同步的机制,如果在一个 vsync 时间内,cpu 或者 gpu 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。

因此,我们需要平衡 cpu 和 gpu 的负荷避免一方超负荷运算。为了做到这一点,我们首先得了解 cpu 和 gpu 各自负责哪些内容。

iOS性能优化教程之页面加载速率详解

上面的图展示了 ios 系统下各个模块所处的位置,下面话不多说了,来一起看看本文的正文。

之前搜罗了网上很多关于ios性能优化方面的资料 ,本人和我的小伙伴们也用了一些时间针对自己的app进行了app的启动速率、页面的加载速率和 页面的帧率方面进行了优化,所以结合了理论和实践,把我们在实践中主要踩过的坑和需要注意的东西 ,总结了一下,希望可以帮到正在准备进行app的性能优化的你。今天主要讲一下app的页面加载速率的优化。

##目的

为了找到真正使我们的app缓慢的原因,我们使用xcode或者一些第三方平台,进行数据测试;

一、页面加载速率的定义

页面加载速率:关于页面的加载速度的统计,我们是测试一个viewcontroller从viewdidload的第一行到viewdidappear的最后一行所用的时间。

二、页面加载速率的目标值

目标:页面加载速率最好完美的时间在0.3s左右

为了弄明白,到底是什么原因让我们的app,页面加载速度相对来说比较慢,我们对页面的ui进行优化,数据也进行了异步加载,我们hook数据一看,页面的加载速度果然有所减少,但是减少的值大概只有0.03s,很明显这个值不足以达到我们想要的效果,后来,通过写了一些测试demo,针对空白页面和有ui创建的页面进行各种对比后,似乎和我们页面加载过程中的push动画有很大的关系;下面所做的实验主要是为了验证这个问题,针对这个问题,我选取了我们工程的一个类,对有push进入到这个页面有过场动画和没有动画进行测试,以下数据是测试结果:

iOS性能优化教程之页面加载速率详解

通过这个实验,我们可以看出,不加动画的话,我们的页面加载的速度可以说是没有任何的卡顿,超级迅速,但是如果把过场动画给打开,单是动画的时间就是在0.5s左右,而s我们是希望用户在点击跳转页面的时候,目标是页面在0.3s左右呈现,这如果加动画,这个目标很难达到;不过通过查找相关资料,我们证实了我们可以把如果有过场动画的页面,去掉动画,而是通过我们自己去给用户添加一个过场动画,而这个时间是可以受到我们自己的控制,而不是傻傻的等动画结束后再加载页面内容。的这就是说,可以一边动画的时候,一边已经开始加载页面相关东西了,这样可以大大的优化页面加载时间。

三、优化前数据

iOS性能优化教程之页面加载速率详解

四、 优化后的数据

iOS性能优化教程之页面加载速率详解

到这里 ,你一定想问 :我该如何hook数据的???

五、如何进行数据的收集

给uiviewcontroller 创建一个分类 eg :uiviewcontroller+swizzle

代码如下

?
1
2
3
4
5
6
7
#import <uikit/uikit.h>
#import <objc/runtime.h>
 
@interface uiviewcontroller (swizzle)
@property(nonatomic,assign) cfabsolutetime viewloadstarttime;
 
@end
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#import "uiviewcontroller+swizzle.h"
#import <objc/runtime.h>
 
static char *viewloadstarttimekey = "viewloadstarttimekey";
@implementation uiviewcontroller (swizzle)
-(void)setviewloadstarttime:(cfabsolutetime)viewloadstarttime{
objc_setassociatedobject(self, &viewloadstarttimekey, @(viewloadstarttime), objc_association_copy);
 
}
-(cfabsolutetime)viewloadstarttime{
return [objc_getassociatedobject(self, &viewloadstarttimekey) doublevalue];
}
+ (void)load
{
static dispatch_once_t oncetoken;
dispatch_once(&oncetoken, ^{
sel origsel = @selector(viewdidappear:);
sel swizsel = @selector(swiz_viewdidappear:);
[uiviewcontroller swizzlemethods:[self class] originalselector:origsel swizzledselector:swizsel];
 
sel vcwillappearsel=@selector(viewwillappear:);
sel swizwillappearsel=@selector(swiz_viewwillappear:);
[uiviewcontroller swizzlemethods:[self class] originalselector:vcwillappearsel swizzledselector:swizwillappearsel];
 
sel vcdidloadsel=@selector(viewdidload);
sel swizdidloadsel=@selector(swiz_viewdidload);
[uiviewcontroller swizzlemethods:[self class] originalselector:vcdidloadsel swizzledselector:swizdidloadsel];
 
sel vcdiddisappearsel=@selector(viewdiddisappear:);
sel swizdiddisappearsel=@selector(swiz_viewdiddisappear:);
[uiviewcontroller swizzlemethods:[self class] originalselector:vcdiddisappearsel swizzledselector:swizdiddisappearsel];
 
sel vcwilldisappearsel=@selector(viewwilldisappear:);
sel swizwilldisappearsel=@selector(swiz_viewwilldisappear:);
[uiviewcontroller swizzlemethods:[self class] originalselector:vcwilldisappearsel swizzledselector:swizwilldisappearsel];
});
}
 
+ (void)swizzlemethods:(class)class originalselector:(sel)origsel swizzledselector:(sel)swizsel
{
method origmethod = class_getinstancemethod(class, origsel);
method swizmethod = class_getinstancemethod(class, swizsel);
 
//class_addmethod will fail if original method already exists
bool didaddmethod = class_addmethod(class, origsel, method_getimplementation(swizmethod), method_gettypeencoding(swizmethod));
if (didaddmethod) {
class_replacemethod(class, swizsel, method_getimplementation(origmethod), method_gettypeencoding(origmethod));
} else {
//origmethod and swizmethod already exist
method_exchangeimplementations(origmethod, swizmethod);
}
}
 
- (void)swiz_viewdidappear:(bool)animated
{
[self swiz_viewdidappear:animated];
if (self.viewloadstarttime) {
cfabsolutetime linktime = (cacurrentmediatime() - self.viewloadstarttime);
 
nglog(@" %f s--------------------ssssss %@:速度:   %f s",self.viewloadstarttime, self.class,linktime );
self.viewloadstarttime = 0;
}
}
 
-(void)swiz_viewwillappear:(bool)animated
{
[self swiz_viewwillappear:animated];
}
 
-(void)swiz_viewdiddisappear:(bool)animated
{
[self swiz_viewdiddisappear:animated];
}
 
-(void)swiz_viewwilldisappear:(bool)animated
{
[self swiz_viewwilldisappear:animated];
}
-(void)swiz_viewdidload
{
self.viewloadstarttime =cacurrentmediatime();
nslog(@" %@swiz_viewdidload starttime:%f",self.class, self.viewloadstarttime );
[self swiz_viewdidload];
}
 
@end

##如何进行优化

方法:充分利用push 动画的时间 ,使页面在进入的时候,同事进行类似push 动画,这样可以充分减少页面的加载速度(不包括网络请求时间,网络的请求的时间我们这边不好控制)。

具体实现代码如下

重写 push方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)pushviewcontroller:(uiviewcontroller *)viewcontroller animated:(bool)animated {
 
if (self.viewcontrollers.count > 0) {
viewcontroller.hidesbottombarwhenpushed = yes;
if (animated) {
 
catransition *animation = [catransition animation];
animation.duration = 0.4f;
animation.timingfunction = [camediatimingfunction functionwithname:kcamediatimingfunctioneaseineaseout];
animation.type = kcatransitionpush;
animation.subtype = kcatransitionfromright;
[self.navigationcontroller.view.layer addanimation:animation forkey:nil];
[self.view.layer addanimation:animation forkey:nil];
[super pushviewcontroller:viewcontroller animated:no];
return;
}
}
[super pushviewcontroller:viewcontroller animated:animated];
}

通过控制台 ,我们就可以看到页面的加载的速度了,主要的方法是swiz_viewdidload  和swiz_viewdidappear

六、优化后的结果

iOS性能优化教程之页面加载速率详解

七、结果分析

我们可以看出,我们的页面的viewdidappear是在过场动画结束后被调用的,而过场动画的持续时间是0.5秒左右。所以我们的页面平均在0.8秒左右的页面,如果要优化得更好,我们可以看有没有方法解决这个问题,如果能替换掉动画,让动画在进行的过程中 ,页面的加载也在异步的进行中,这样 我们就可以缩短页面的加载时间了;注:但这个加载对加载h5的页面不适用;

总结:

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。

原文链接:https://juejin.im/post/5b8be6c86fb9a019d9247b2a

延伸 · 阅读

精彩推荐
  • IOSIOS网络请求之AFNetWorking 3.x 使用详情

    IOS网络请求之AFNetWorking 3.x 使用详情

    本篇文章主要介绍了IOS网络请求之AFNetWorking 3.x 使用详情,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    总李写代码6892021-03-04
  • IOSiOS APP实现微信H5支付示例总结

    iOS APP实现微信H5支付示例总结

    这篇文章主要介绍了iOS APP实现微信H5支付示例总结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下...

    一张小A11332021-06-01
  • IOSiOS10 Xcode8适配7个常见问题汇总

    iOS10 Xcode8适配7个常见问题汇总

    这篇文章主要为大家详细汇总了iOS10 Xcode8适配7个常见问题,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    索马里猫10332021-02-01
  • IOSiOS中时间与时间戳的相互转化实例代码

    iOS中时间与时间戳的相互转化实例代码

    这篇文章主要介绍了iOS中时间与时间戳的相互转化实例代码,非常具有实用价值,需要的朋友可以参考下。...

    张无忌!4812021-03-09
  • IOSiOS常见的几个修饰词深入讲解

    iOS常见的几个修饰词深入讲解

    这篇文章主要给大家介绍了关于iOS常见的几个修饰词的相关资料,iOS修饰词包括assign、weak、strong、retain、copy、nonatomic、atomic、readonly、readwrite,文中通过示...

    郡王丶千夜7422021-05-10
  • IOSiOS逆向教程之logify跟踪方法的调用

    iOS逆向教程之logify跟踪方法的调用

    这篇文章主要给大家介绍了关于iOS逆向教程之logify跟踪方法调用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学...

    Mr.Guo11472021-04-28
  • IOS谈一谈iOS单例模式

    谈一谈iOS单例模式

    这篇文章主要和大家谈一谈iOS中的单例模式,单例模式是一种常用的软件设计模式,想要深入了解iOS单例模式的朋友可以参考一下...

    彭盛凇11872021-01-19
  • IOSxcode8提交ipa失败无法构建版本问题的解决方案

    xcode8提交ipa失败无法构建版本问题的解决方案

    xcode升级到xcode8后发现构建不了新的版本。怎么解决呢?下面小编给大家带来了xcode8提交ipa失败无法构建版本问题的解决方案,非常不错,一起看看吧...

    Cinna丶7542021-02-03