Time Profiler分析原理:它按照固定的时间间隔来跟踪每一个线程的堆栈信息,通过统计比较时间间隔之间的堆栈状态,来推算某个方法执行了多久,并获得一个近似值。

平常采用instrument中的core animation进行性能检测

着重优化点:

1、图层的混合:"Color Blended Layers"正是用于检测哪里发生了图层混合,并用红色标记出来。

  控件不要设置透明度,默认值opaque = true,记得设置控件的backgroundColor。确保UIImage没有alpha通道。

2、光栅化:Color Hits Green and Misses Red”,它表示如果命中缓存则显示为绿色,否则显示为红色

  将一个layer预先渲染成位图(bitmap),然后加入缓存中。如果对于阴影效果这样比较消耗资源的静态内容进行缓存,可以得到一定幅度的性能提升。

  光栅化仅适用于较复杂的、静态的效果。

3、正确的图片格式:比如png,jpeg。从网络下载的图片,而GPU恰好不支持这个格式,这就需要CPU预先进行格式转化。“Color Copied Images”就用来检测这种实时的格式转化,如果有则会将图片标记为蓝色。

4、正确的图片尺寸:“Color Misaligned Images”。它表示如果图片需要缩放则标记为黄色,如果没有像素对齐则标记为紫色。

5、离屏渲染:“Color Offscreen-Rendered Yellow”会把需要离屏渲染的地方标记为黄色。

6、手动进行图片解码:采用异步绘制,在display中进行图片的解码操作,主要使用bitmap和colorspace等手段。

7、尤其在scrollview中避免使用xib:直接从代码创建和读文件创建,肯定读文件要慢很多,

8、尤其在复杂视图中不使用autolayout:增加cpu的计算任务

9、采用coretext代替label:label需要计算他的size,进行布局

10、避免动态创建视图:懒加载,设置hidden属性

11、必要时layer代替view:

12、复杂视图必要时合为一张图片处理:

13、异步操作db,计算等工作

14、通知,VoIP,定位,蓝牙等都会使设备从 Standby 状态唤起

15、imageNamed: 与 imageWithContentsOfFile:的差异

16、少用NSDateFormatter

17、不要随意使用 NSLog()

18、[NSFileManager attributesOfItemAtPath:error:] 会浪费大量时间读取可能根本不需要的附加属性:

#import <sys/stat.h>
struct stat statbuf;
const char *cpath = [filePath fileSystemRepresentation];
if (cpath && stat(cpath, &statbuf) == ) {
NSNumber *fileSize = [NSNumber numberWithUnsignedLongLong:statbuf.st_size];
NSDate *modificationDate = [NSDate dateWithTimeIntervalSince1970:statbuf.st_mtime];
NSDate *creationDate = [NSDate dateWithTimeIntervalSince1970:statbuf.st_ctime];
// etc
}

19、内存泄漏工具:MLeakFinder

20、尽量少使用定时器:会频繁唤醒runloop执行任务

21、异步绘制:VVeboTableViewDemo :异步计算空间frame,将不交互的空间绘制到一张图片中

21、tableview方面:

  高度缓存:预先layout、或者UITableView-FDTemplateLayoutCell

  cell中不要使用drawRect绘制图片,改用CALayer-drawInContext:Core Animation将会为这个图层申请一个后备存储,用来保存那些方法绘制进来的位图。那些方法内的代码将会运行在 CPU上,结果将会被上传到GPU

  按需加载:VVeboTableViewDemo : 监听runloop的default模式,预加载指定行的样式

 
 

离屏渲染:

真正的离屏渲染发生在GPU。

如果要在显示屏上显示内容,我们至少需要一块与屏幕像素数据量一样大的frame buffer,作为像素数据存储区域,而这也是GPU存储渲染结果的地方。如果有时因为面临一些限制,无法把渲染结果直接写入frame buffer,而是先暂存在另外的内存区域,之后再写入frame buffer,那么这个过程被称之为离屏渲染。

常见的触发离屏渲染场景:

1、cornerRadius+clipsToBounds:

容器的子layer因为父容器有圆角,那么也会需要被裁剪,而这时它们还在渲染队列中排队,尚未被组合到一块画布上,自然也无法统一裁剪。

此时我们就不得不开辟一块独立于frame buffer的空白内存,先把容器以及其所有子layer依次画好,然后把四个角“剪”成圆形,再把结果画到frame buffer中。

2、shadow:

阴影默认是作用在其中”非透明区域“的,而且需要显示在所有layer内容的下方,阴影的本体(layer和其子layer)都还没有被组合到一起,怎么可能在第一步就画出只有完成最后一步之后才能知道的形状呢?这样一来又只能另外申请一块内存,把本体内容都先画好,再根据渲染结果的形状,添加阴影。不过如果我们能够预先告诉CoreAnimation(通过shadowPath属性)阴影的几何形状,那么阴影当然可以先被独立渲染出来,不需要依赖layer本体。

3、group opacity:

alpha并不是分别应用在每一层之上,而是只有到整个layer树画完之后,再统一加上alpha,最后和底下其他layer的像素进行组合。显然也无法通过一次遍历就得到最终结果。

4、mask:

我们知道mask是应用在layer和其所有子layer的组合之上的,而且可能带有透明度,那么其实和group opacity的原理类似。

5、渐变。

6、shouldRasterize

尽管离屏渲染开销很大,但是当我们无法避免它的时候,可以想办法把性能影响降到最低。

shouldRasterize。一旦被设置为true,Render Server就会强制把layer的渲染结果(包括其子layer,以及圆角、阴影、group opacity等等)保存在一块内存中,这样一来在下一帧仍然可以被复用,而不会再次触发离屏渲染。

layer的内容(包括子layer)必须是静态的,因为一旦发生变化(如resize,动画),之前辛苦处理得到的缓存就失效了。

如果layer的子结构非常复杂,渲染一次所需时间较长,同样可以打开这个开关,把layer绘制到一块缓存,然后在接下来复用这个结果,这样就不需要每次都重新绘制整个layer树了。

开启“Color Hits Green and Misses Red”来检查该场景下光栅化操作是否是一个好的选择。绿色表示缓存被复用,红色表示缓存在被重复创建。

启动和优化:

iOS的启动流程

1、根据 info.plist 里的设置加载闪屏,建立沙箱,对权限进行检查等

2、加载可执行文件

3、加载动态链接库,进行 rebase 指针调整和 bind 符号绑定

4、Objc 运行时的初始处理,包括 Objc 相关类的注册、category 注册、selector 唯一性检查等;

5、初始化,包括了执行 +load() 方法、attribute((constructor)) 修饰的函数的调用、创建 C++ 静态全局变量。

6、执行 main 函数

7、Application 初始化,到 applicationDidFinishLaunchingWithOptions 执行完

8、初始化帧渲染,到 viewDidAppear 执行完,用户可见可操作。

启动优化

1、减少动态库的加载

2、减少系统库和第三方库的加载

3、去除掉无用的类和C++全局变量的数量,减少项目文件中Category,静态变量等的使用数量

4、尽量让load方法中的内容放到首屏渲染之后再去执行,或者使用initialize替换

5、去除在首屏展现之前非必要的功能

6、检查首屏展现之前主线程的耗时方法,将没必要的耗时方法滞后或者延迟执行

AutoLayout:

我们知道view的frame属性是CGRect类型,只包含origin(x,y)和size(width,height),万变不离其宗,Auto Layout的线性表达式,最终也是求解出这四个值,然后布局视图的,我们看个简单的例子:
 

通过Auto Layout来布局的过程就是将上述等式转换为frame的过程,这时我们可以注意到,如果求解ViewA的frame需要解4元1次方程组,而求解ViewB的frame需要解6元1次方程组,这还是在width和height都是直接设置的原因,如果这两个约束也依赖ViewA的话,就要求解8元1次方程组了.
 
如果页面子视图超过30个,autolayout会造成卡顿的情况。在ios12之后,页面子视图在100个之类,autolayout的性能都是ok的。
 
编译过程: (我不懂这一块的东西)
  • 预编译:主要处理以“#”开始的预编译指令。
  • 编译:
    1. 词法分析:将字符序列分割成一系列的记号。
    2. 语法分析:根据产生的记号进行语法分析生成语法树。
    3. 语义分析:分析语法树的语义,进行类型的匹配、转换、标识等。
    4. 中间代码生成:源码级优化器将语法树转换成中间代码,然后进行源码级优化,比如把 1+2 优化为 3。中间代码使得编译器被分为前端和后端,不同的平台可以利用不同的编译器后端将中间代码转换为机器代码,实现跨平台。
    5. 目标代码生成:此后的过程属于编译器后端,代码生成器将中间代码转换成目标代码(汇编代码),其后目标代码优化器对目标代码进行优化,比如调整寻址方式、使用位移代替乘法、删除多余指令、调整指令顺序等。
  • 汇编:汇编器将汇编代码转变成机器指令。
  • 静态链接:链接器将各个已经编译成机器指令的目标文件链接起来,经过重定位过后输出一个可执行文件。
  • 装载:装载可执行文件、装载其依赖的共享对象。
  • 动态链接:动态链接器将可执行文件和共享对象中需要重定位的位置进行修正。
  • 最后,进程的控制权转交给程序入口,程序终于运行起来了。

异步绘制:

改变frame、更新UIView/CALayer,或者自己去调用setNeedsLayout/setNeedsDisplay方法,内部调用流程为:

当前runloop即将结束的时候调用CALayerdisplay方法;

系统绘制流程为:

异步绘制的原理。 我们不能在非主线程将内容绘制到layer的context上,但是我们可以将需要绘制的内容绘制在一个自己创建的跑private_context上。通过CGBitmapContextCreate()可以创建一个CGCentextRef,在异步线程使用这个context进行绘制。
- (void)display {
dispatch_async(backgroundQueue, ^{
UIGraphicsBeginImageContextWithOptions(size, NO, scale);
///获取当前上下文
CGContextRef context = UIGraphicsGetCurrentContext();
///将坐标系反转
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
///文本沿着Y轴移动
CGContextTranslateCTM(context, 0, size.height);
///文本反转成context坐标系
CGContextScaleCTM(context, 1.0, -1.0);
///创建绘制区域
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0, 0, size.width, size.height));
///创建需要绘制的文字
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:self.asynText];
[attStr addAttribute:NSFontAttributeName value:self.asynFont range:NSMakeRange(0, self.asynText.length)];
[attStr addAttribute:NSBackgroundColorAttributeName value:self.asynBGColor range:NSMakeRange(0, self.asynText.length)];
///根据attStr生成CTFramesetterRef
CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attStr);
CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, attStr.length), path, NULL);
///将frame的内容绘制到content中
CTFrameDraw(frame, context);
UIImage *getImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
///子线程完成工作, 切换到主线程展示
dispatch_async(dispatch_get_main_queue(), ^{
self.layer.contents = (__bridge id)getImg.CGImage;
});
});
}

网络层的优化:

减少DNS请求,使用HttpDNS获取原始ip地址进行请求,在header请求头中增加host字段,指定为原始请求地址

数据传输使用gzip

合并请求,减少请求并发数(埋点SDK,crash日志收集)

使用断点续传,否则网络不稳定时可能多次传输相同的内容

结合ETag 和 Last-Modified 减少数据重复传输

网络请求本地缓存

避免频繁发起请求

请求失败缓存后重发请求

2G、3G、4G、wifi下设置不同的超时时间

弱网环境下,减少并发请求

http2本身支持的长连接,多路复用机制

网络不可用时,不要进行网络请求

上传和下载大数据时,一次下载多点

app优化篇的更多相关文章

  1. 百度APP移动端网络深度优化实践分享(二):网络连接优化篇

    本文由百度技术团队“蔡锐”原创发表于“百度App技术”公众号,原题为<百度App网络深度优化系列<二>连接优化>,感谢原作者的无私分享. 一.前言 在<百度APP移动端网 ...

  2. 百度APP移动端网络深度优化实践分享(一):DNS优化篇

    本文由百度技术团队“蔡锐”原创发表于“百度App技术”公众号,原题为<百度App网络深度优化系列<一>DNS优化>,感谢原作者的无私分享. 一.前言 网络优化是客户端几大技术方 ...

  3. 百度APP移动端网络深度优化实践分享(三):移动端弱网优化篇

    本文由百度技术团队“蔡锐”原创发表于“百度App技术”公众号,原题为<百度App网络深度优化系列<三>弱网优化>,感谢原作者的无私分享. 一.前言 网络优化解决的核心问题有三个 ...

  4. 【转】Android性能优化之布局优化篇

     转自:http://blog.csdn.net/feiduclear_up/article/details/46670433 Android性能优化之布局优化篇 分类: andorid 开发2015 ...

  5. 【HELLO WAKA】WAKA iOS客户端 之一 APP分析篇

    由于后续篇幅比较大,所以调整了内容结构. 全系列 [HELLO WAKA]WAKA iOS客户端 之一 APP分析篇 [HELLO WAKA]WAKA iOS客户端 之二 架构设计与实现篇 [HELL ...

  6. HTML5进阶(三)HBuilder实现软件自动升级(优化篇)

    HBuilder实现软件自动升级(优化篇) 前言 受前篇博客<HTML5进阶(二)HBuilder实现软件自动升级>(点击查看详情)的影响,测试过程中发现APP自动更新还是存在问题,第一次 ...

  7. Hybrid APP基础篇(二)->Native、Hybrid、React Native、Web App方案的分析比较

    说明 Native.Hybrid.React.Web App方案的分析比较 目录 前言 参考来源 前置技术要求 楔子 几种APP开发模式 概述 Native App Web App Hybrid Ap ...

  8. Android App优化之ANR详解

    引言 背景:Android App优化, 要怎么做? Android App优化之性能分析工具 Android App优化之提升你的App启动速度之理论基础 Android App优化之提升你的App ...

  9. Hybrid APP基础篇(四)->JSBridge的原理

    说明 JSBridge实现原理 目录 前言 参考来源 前置技术要求 楔子 原理概述 简介 url scheme介绍 实现流程 实现思路 第一步:设计出一个Native与JS交互的全局桥对象 第二步:J ...

随机推荐

  1. “2014年CityEngine三维建模与设计精英培训班”——全国巡回举办

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYXJjZ2lzX2FsbA==/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...

  2. 【转载】Hadoop官方文档翻译——HDFS Architecture 2.7.3

    HDFS Architecture HDFS Architecture(HDFS 架构) Introduction(简介) Assumptions and Goals(假设和目标) Hardware ...

  3. golang:iconv

    最近在做邮件解析的工作,遇到需要转字符集编码的情况,go官方好像没有提供这样的库,于是从github上找了一下. https://github.com/qiniu/iconv 开发环境: linux ...

  4. Git命令速查表

  5. perl 里面如何写出阅读友好的代码提示

    在我们使用别人写好的程序时,经常会使用-h 之类的东西查看一下简单的帮助手册或者说明信息: 对于perl 语言而言,写起来简单,经常随手一写,解决了当时的问题,但是过几天去看,你都不知道这个脚本该怎么 ...

  6. C# 获取对象 大小 Marshal.SizeOf (sizeof 只能在不安全的上下文中使用)

    C# 能否获取一个对象所占内存的大小? 今日,在项目重构的时候忽然想到一个问题,一个类哪些成员的增加,会影响一个类所占内存的大小?C#有没有办法知道一个对象占多少内存呢? 第一个问题:很快想到是类的非 ...

  7. bootstarpTable load data

    <!doctype html> <html lang="en"> <head> <!-- Required meta tags --> ...

  8. 自动化测试工具Ranorex的录制功能使用

    由于帆软的 Report 包含gui和web端 设计器 web预览 做自动化测试不适合使用 Katalon 发现了Ranorex Ranorex 是一款在Windows操作系统的上运行的GUI自动测试 ...

  9. nginx && apache 图片代理

    location ~ /mmopen/ { proxy_set_header Host thirdwx.qlogo.cn; rewrite /(.+)$ /$ break; proxy_pass ht ...

  10. hello 2019 D

    一开始sb了考虑总的因子疯狂T,做题太少了...没意识到会有辣么多因子... 神仙说1e9以内的最多的就有800个因子的了... 然后我们可以考虑质因子 我觉得已经说得很明白了... 唔逆元好像exg ...