热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发完iOS应用,接下去你该做的事

iOS专项总结关于analyzeClang静态分析器SlenderFauxPasWarningLeaksTimeProfiler加载时间iOSApp启动过程帧率等如何优雅地显示帧率标签?iO
  • iOS专项总结
    • 关于 analyze
    • Clang 静态分析器
    • Slender
    • Faux Pas
    • Warning
    • Leaks
    • Time Profiler
    • 加载时间
      • iOS App启动过程
    • 帧率等
      • 如何优雅地显示帧率标签?

iOS专项总结

  一个应用经过多次迭代后告一段落,接下去我们在技术上还可以做些什么呢?答案是提高代码的整体质量。关于这方面,除了我们常喊的 重构,测试也非常重要。

  博主近期给我们的 iOS客户端代码来了一次专项测试。主要从常规的 辅助测试 入手,来了次代码大清理,找到代码中的问题,并一一改掉它们。惊喜的是,这对于提高本人的代码水平有很大的帮助。其实,这套代码的质量本身已经很高了,也非常整洁。而这主要得益于严格的代码规范和pull request机制。

  关于测试,App常关注的往往是一些功能性的,包括单元测试,用monkey在界面上点击看页面表现是否正确等等。 我以前还搭过一个 aspectJ + robotium。(这是Java上的) 然而,测试更应该覆盖代码质量,性能检测等等。
  下面给出一副我理解测试的结构图:

  点击图片可放大缩小。

顺着这幅图,我们可以从静态测试入手。

 

关于 analyze

在Xcode 中执行product -> analyze工具,出现了粗粗细细的箭头标识表示调用逻辑。

image

这个工具所找出的主要是代码中的逻辑问题,本身能发现的问题很多。实际运用到工程代码上,发现的问题主要分为以下四点:

  • 比如初始化赋值未被使用过。
  • 比如未调用super语句。
  • 比如从未使用过的变量。
  • 比如逻辑上错误的代码。(这点极少)

但是关于资源文件,该工具是扫描不出来的。另外,我们可以在Xcode中配置该工具。

 

Clang 静态分析器

analyze 的底层其实就是 Clang 静态分析器,包含 ’shallow’ 和 ‘deep’ 深浅两种模式。

深模式比浅模式慢很多,因为多出了跨方法的 控制流分析 以及 数据流分析。(如前文那副图)

建议:
开启分析器的 全部检查(方法是在 build setting 的 “Static Analyzer” 部分开启所有选项)
在 build setting 里,对 release 的 build 配置开启 “Analyzer during ‘Build’“。(真的,一定要这样做 — 你不会记得手动跑分析器的。)
把 build setting 里的 “Model of Analysis for ‘Analyze’“设为 Shallow(faster)
把 build setting 里的 “Model of Analysis for ‘Build’“设为 Deep

但这两种对于公司这么一个大项目来说都不快,假如在 build setting里 开启 Model of Analysis for ‘Build’ 并设为浅模式,那么每次运行就会整体跑一遍静态分析并不合理。

还有一种设想是在打 ad hoc 包前自动跑一遍 静态分析,是比较合理的也可以在XCode中开启。但是我们编译打包用的是自动化工具,服务器直接从github读取而不是通过XCode,会跳过这个分析。
比较合理的做法是定期 跑一遍 analyzer 工具。(比如每周五)而不是 build , release 以前做这事。(问了下其他同事的意见)

目前静态分析在工程中有 这么几块不改的问题: 首先是网上拷过来的算法。包括加密算法,模糊算法,64位编译算法等等。里面有一些远大于,远小于,与运算等。看不懂的地方不改。 

为什么不改呢?因为没看出逻辑问题。(或者看懂了也不敢改,怕算法错误。)

 

Slender

slender 是一款针对 iOS 图片分析的 Mac 软件。导入工程,可以找到所有未使用到的 resource。(unused 这软件也可以找到多余的资源)

除此以外,它还给出了所有资源文件的建议。包括你缺省的 x3, x2图片版本等。

image

 

Faux Pas

意外发现的惊喜。Faux Pas 是一个出色的静态 Error 检测工具。

slender 找到的是未使用的资源,faux 找到的是代码中使用到,但不存在的image。(实测下来往往有很多)

甚至由此 发现了一些弃用的文件夹。

不仅如此,它有一百多个error 和 warning 规则。包括 未定义的 user define runtime attributes(跑在runtime上)。这一点平时靠程序员的肉眼狠难见。曾经你为了图省事直接在 xib 中炫技,加上了一些 key。然而当代码变迁,删掉一个类的时候,根本想不到 到 xib中去删除 对应的部分。

还有一些代码规范上的问题。

比如界面上的文字未本地化;在release 版本依旧有NSLog 输出;NSPhotoLibraryUsageDescription在info.plist中未定义;被定义成strong类型的delegate等等。

和一些配置上的建议。

 

Warning

你的pods 中还在报 warning 吗?你可以使用下面这种方法屏蔽它们。

在podfile 的开头加上这样一句话:

inhibit_all_warnings!

就能屏蔽 pods 中所有代码上的 warning了。

然后可惜的是,对于编译上的 warning却束手无策。注:iOS中不能阻止 Xcode 报warning 的选项。只能屏蔽某一文件,或某工程的warning。比如我们已屏蔽的pods中 代码的错误。但是屏蔽不了系统在编译过程中 非代码级别的 警告。

并且也不建议屏蔽这种警告,比如由于友盟未支持 7.0 版本所导致的 警告。现在由于 友盟本身未支持,7.0 用户还有许多等等原因,但未来或许能解决。不应忘记这点。

 

Leaks

苹果集成了一个性能检测工具,叫instruments。使用instrument中的Leaks工具:

image

iOS内存泄露点比较少,大部分都是系统内部方法,或者一些库里的。我们对此别无办法。实测下来,主要的泄露场景集中在 UIKeyboard 上。在我尝试了各种点击场景后,系统弹框时候最容易泄露。

假如你的代码中真有泄露,那它们可能集中在快速滑动 Listing的时候,加载大批量图片的时候。在我们的应用中,这些大都使用了网上开源的成熟库。

对于 iOS可能泄露的地方,我的建议还是通过已知的代码规范 逆向去寻找。比如 误用 strong类型的delegate; block 中没有使用 weak self等等。

 

Time Profiler

时间都去哪儿了。这个工具可以找出我们最耗时的操作并定位到代码中。

实际使用过程中,确实发现了一处不合理的耗时循环。

image

(右侧黑色部分是耗时操作,但不一定是错误的代码)

工具只是统计时间的消耗,更重要的是通过对代码的敏感,分析定位出可优化的代码。举个例子,有这么一段代码。

有个循环在判断时候使用了枚举类型,但由于枚举类型获取不到count,所以直接写死了循环10次。

但其实我们可以在枚举类型的末尾加上TYPE_MAX。这样既拥有了枚举在switch时候的优势,又得到了NSArray获取count的办法。

 

加载时间

加载时间上,主要看看不同网络下出首页有没有卡死等。公司wifi条件真机上测试大约 1.2s 内加载完毕,而当模拟边缘网络时主线程加载依然迅速,没发现什么问题。

关于启动时间,有两种查看方法。一种是通过上文的时间检测工具,另一种是直接在代码中打log。其中log又有两种打法。

分别说下这两种打法:

CFAbsoluteTime StartTime;
int main(int argc, char **argv) {
StartTime = CFAbsoluteTimeGetCurrent();
// ...
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Launched in %f sec", CFAbsoluteTimeGetCurrent() - StartTime);
});
// ...
}

这是通常的打印方法。然而,从main 开始启动有太多的不可控因素。

iOS App启动过程

  • 链接并加载Framework和static lib
  • UIKit初始化
  • 应用程序callback
  • 第一个Core Animation transaction

其中 framework的加载过程我们难以控制,而初始化字体、状态栏、user defaults、main nib等的过程,我们也不需要关心。当然,你可以做的是保持它们尽可能小,没有冗余。

所以,还有一种做法是记录你在 didFinishLaunchingWithOptions 中所耗费的时间。在这里,你可能初始化了许多不必须的第三方库,做了几次网络请求。而这些,我们都可以 lazyLoad,让程序尽快地冷启动。

 

帧率等

如何优雅地显示帧率标签?

image

在 QuartzCore.framework里,有一个 CADisplayLink 类。系统在渲染每一帧的时候,都会调用一次CADisplayLink中的 selector, 它有点像一个定时器类,默认为一秒调用60次。

帧率大部分时间都稳定在 60fps,然而有两种情况下会导致它不能以每秒60次的频率调用回调方法。

  1. CPU忙于其他计算,无暇执行屏幕绘制动作。
  2. 执行回调方法所用时间大于重绘每帧的间隔时间。

我们可以利用这个类的特性,写出如下代码来实时显示一个 屏幕帧率 的标签。

CADisplayLink displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];  
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];

- (void)tick:(CADisplayLink *)displayLink {
//...
self.numberOfFrames++;
NSTimeInterval delta = displayLink.timestamp - self.lastTimestamp;

// Less than one second
if (delta <1) {
return;
}

CGFloat fps = self.numberOfFrames / delta;

//Update label
....
}

这个fps 的数值,便是每秒帧数。实测当下拉刷新,或加载List过快时,都会往下掉得很厉害。而这便是性能瓶颈所在的关键点。

 

 本文系冰洁原创,遵循 署名-非商业性知识共享进行许可. 转载请在文章开头显眼处注明注明作者和出处 【冰洁】http://www.bingjie.me


推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • GreenDAO快速入门
    前言之前在自己做项目的时候,用到了GreenDAO数据库,其实对于数据库辅助工具库从OrmLite,到litePal再到GreenDAO,总是在不停的切换,但是没有真正去了解他们的 ... [详细]
  • Java 11相对于Java 8,OptaPlanner性能提升有多大?
    本文通过基准测试比较了Java 11和Java 8对OptaPlanner的性能提升。测试结果表明,在相同的硬件环境下,Java 11相对于Java 8在垃圾回收方面表现更好,从而提升了OptaPlanner的性能。 ... [详细]
  • ShiftLeft:将静态防护与运行时防护结合的持续性安全防护解决方案
    ShiftLeft公司是一家致力于将应用的静态防护和运行时防护与应用开发自动化工作流相结合以提升软件开发生命周期中的安全性的公司。传统的安全防护方式存在误报率高、人工成本高、耗时长等问题,而ShiftLeft提供的持续性安全防护解决方案能够解决这些问题。通过将下一代静态代码分析与应用开发自动化工作流中涉及的安全工具相结合,ShiftLeft帮助企业实现DevSecOps的安全部分,提供高效、准确的安全能力。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了如何清除Eclipse中SVN用户的设置。首先需要查看使用的SVN接口,然后根据接口类型找到相应的目录并删除相关文件。最后使用SVN更新或提交来应用更改。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • 本文介绍了在Mac上安装Xamarin并使用Windows上的VS开发iOS app的方法,包括所需的安装环境和软件,以及使用Xamarin.iOS进行开发的步骤。通过这种方法,即使没有Mac或者安装苹果系统,程序员们也能轻松开发iOS app。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • Annotation的大材小用
    为什么80%的码农都做不了架构师?最近在开发一些通用的excel数据导入的功能,由于涉及到导入的模块很多,所以开发了一个比较通用的e ... [详细]
author-avatar
mobiledu2502861137
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有