iOS 项目优化

前言

近期正处于一段工作空白区,也想着学习学习一下项目优化,所以就自己的项目出手,一步一步地优化项目。

一、项目结构与应用包瘦身

项目结构

项目本身首先划分功能区以PageCoreApp划分
1、Page存储应用的模块,包含首页个人中心等,每个模块下再以ControllerViewModel划分
2、Core 存储着一些与项目业务、界面无关的类,包括分类宏定义封装的请求基类
3、App则存储着一些与项目相关的类,包括APIBase基类

应用包瘦身
1、首先找出项目中未使用的图片:

这里使用了python脚本 脚本地址 找出项目中未使用的图片,结果不是非常准确,但是可以自行判断(存在图片使用是根据服务端返回显示的情况等)

使用方法:

1、下载链接的unUseImage.py文件
2、修改项目路径以及输出路径
3、终端执行 python unUseImage.py
使用后会在路径里输出文件:

未使用图片

后面发现 这个工具 您可以试试

2、图片无损压缩

这里使用工具 ImageOptim ,点击链接下载
将项目图片拖入优化即可
优化结果:

图片压缩

3、代码瘦身

这里推荐使用LinkMap,可以知道项目中各个类的大小,以权衡是否有替换方案

LinkMap

4、 使用 fui 找到应用中未使用的类

安装fui

sudo gem install fui -n /usr/local/bin

到项目中使用

fui find

即可找出未引用的类,自行判断删除即可

优化前与优化后的ipa包大小对比
优化前
优化后

二、项目性能优化

内存

使用instruments leaks 检测
打开:
1、Product -> Profile -> Leaks
2、点击CellTree、下方筛选
3、定位泄漏代码,修改

Leaks

内存泄漏

工具只是辅助你寻找泄漏的地方,具体是否泄漏还需要自行判断

卡顿

在性能优化中一个最具参考价值的属性是FPS:全称Frames Per Second,其实就是屏幕刷新率,苹果的iphone推荐的刷新率是60Hz,也就是说GPU每秒钟刷新屏幕60次,这每刷新一次就是一帧frame,FPS也就是每秒钟刷新多少帧画面。静止不变的页面FPS值是0,这个值是没有参考意义的,只有当页面在执行动画或者滑动的时候,FPS值才具有参考价值,FPS值的大小体现了页面的流畅程度高低,当低于45的时候卡顿会比较明显。

这里使用Core Animation来检测,注:需使用真机

打开:
1、Product -> Profile -> Core Animation
2、启动应用
3、滑动查看FPS值


FPS

卡顿优化:

1、Color Blended Layers (图层混合)

这个选项是检测哪里发生了图层混合,先介绍一下什么是图层混合?很多情况下,界面都是会出现多个UI控件叠加的情况,如果有透明或者半透明的控件,那么GPU会去计算这些这些layer最终的显示的颜色,也就是我们肉眼所看到的效果。例如一个上层Veiw颜色是绿色RGB(0,255,0),下层又放了一个View颜色是红色RGB(0,0,255),透明度是50%,那么最终显示到我们眼前的颜色是蓝色RGB(0,127.5,127.5)。这个计算过程会消耗一定的GPU资源损耗性能。如果我们把上层的绿色View改为不透明, 那么GPU就不用耗费资源计算,直接显示绿色。
如果出现图层混合了,打开Color Blended Layers选项,那块区域会显示红色,所以我们调试的目的就是将红色区域消减的越少越好。那么如何减少红色区域的出现呢?只要设置控件不透明即可。
1、设置opaque 属性为true。
2、给View设置一个不透明的颜色,没有特殊需要设置白色即可。

eg:

运行应用
在模拟器中找到:


Color Blended Layers

显示运行后的界面:


混合图层

出现了许多混合图层,我们就是要消除这些红色
TIP:当UILabel的内容是中文,需要添加一句 label.layer.masksToBounds = YES,因为当UILabel的内容为中文时,label实际渲染区域要大于label的size,最外层多了一个sublayer,如果不设置第二行label的边缘外层灰出现图层混合的红色,因此需要在label内容是中文的情况下加第二句。单独使用label.layer.masksToBounds = YES是不会发生离屏渲染的

注:xib 也可以直接设置 masksToBounds 在控件的:

xib.masksToBounds

点击+号添加 layer.masksToBounds 打钩即可
优化后的界面:
Color Blended Layers 优化后

相比之前,清爽了不少,图片还需美工提供的图片为无透明的图片。

2、Color Misaligned Images(图片大小)

这个选项可以帮助我们查看图片大小是否正确显示。如果image size和imageView size不匹配,image会出现黄色。要尽可能的减少黄色的出现,因为image size与imageView size不匹配,会消耗资源压缩图片。
选择:


image.png

如果图片出现黄色,可自行将图片压缩至ImageView大小

3、Color Offscreen-Rendered Yellow(离屏渲染)

离屏渲染Off-Screen Rendering 指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。还有另外一种屏幕渲染方式-当前屏幕渲染On-Screen Rendering ,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行。 离屏渲染会先在屏幕外创建新缓冲区,离屏渲染结束后,再从离屏切到当前屏幕, 把离屏的渲染结果显示到当前屏幕上,这个上下文切换的过程是非常消耗性能的,实际开发中尽可能避免离屏渲染。
触发离屏渲染Offscreen rendering的行为:
(1)drawRect:方法
(2)layer.shadow
(3)layer.allowsGroupOpacity or layer.allowsEdgeAntialiasing
(4)layer.shouldRasterize
(5)layer.mask
(6)layer.masksToBounds && layer.cornerRadius

eg:

运行项目,打开:


Color Off-screen Rendered

项目中,此处显示黄色:


离屏渲染

在切圆角时,使用了layer.masksToBounds && layer.cornerRadius
这里我们换一种实现方式,使用贝塞尔曲线画一个边框Layer覆盖在上面即解决了离屏渲染

    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:rect  byRoundingCorners:UIRectCornerAllCorners  cornerRadii:CGSizeMake(cornerRadius, cornerRadius)];
    CAShapeLayer *strokeLayer = [CAShapeLayer layer];
    strokeLayer.path = maskPath.CGPath;
    strokeLayer.fillColor = [UIColor clearColor].CGColor; //内容填充的颜色设置为clear
    strokeLayer.strokeColor = kWhiteColor.CGColor; //边色
    strokeLayer.lineWidth = 1; // 边宽
    [self.layer addSublayer:strokeLayer];

网上还有许多方式可以设置圆角:
1、UIBezierPath + Core Graphics切圆角
2、UIBezierPath + Core Graphics覆盖镂空图片在四角
等等
处理后的效果:

处理离屏渲染后

优化后的FPS
优化后FPS

三、使用RunTime 尽量避免 crash

程序运行中难免会出现崩溃,这里我们可以使用runtime尽量避免一些常见的崩溃错误:

eg:

给NSArray 替换 objectAtIndex:方法

+ (void)load {
    [NSClassFromString(@"__NSArrayI") swapMethod:@selector(objectAtIndex:) currentMethod:@selector(mq_objectAtIndex:)];
}

- (id)mq_objectAtIndex:(NSUInteger)index
{
    if (index >= [self count])
    {
        return nil;
    }
    return [self mq_objectAtIndex:index];
}

+ (void)swapMethod:(SEL)originMethod currentMethod:(SEL)currentMethod;
{
    Method firstMethod = class_getInstanceMethod(self, originMethod);
    Method secondMethod = class_getInstanceMethod(self, currentMethod);
    method_exchangeImplementations(firstMethod, secondMethod);
}

Load方法替换 objectAtIndexmq_objectAtIndex ,当调用objectAtIndex时会走到mq_objectAtIndex,判断是否越界,以此来预防数组越界的crash
其他类像NSDictionary、NSString也可以自行添加

结语

iOS项目优化还有挺多方面的,包括电池优化、启动优化等等,笔者这里就先优化到这里,如果有需求需要优化的话,会再进行更新,谢谢支持!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容