如何提高go代码覆盖率_如何通过静态分析提高iOS代码质量

e6c6dbfae0632d4137d953849e0b7f1c.png随着项目的扩大,依靠人工codereview来保证项目的质量,越来越不现实,这时就有必要借助于一种自动化的代码审查工具:程序静态分析

程序静态分析(Program Static Analysis)是指在不运行代码的方式下,通过词法分析、语法分析、控制流、数据流分析等技术对程序代码进行扫描,验证代码是否满足规范性、安全性、可靠性、可维护性等指标的一种代码分析技术。(来自百度百科)

词法分析,语法分析等工作是由编译器进行的,所以对iOS项目为了完成静态分析,我们需要借助于编译器。对于OC语言的静态分析可以完全通过Clang,对于Swift的静态分析除了Clange还需要借助于SourceKit。

Swift语言对应的静态分析工具是SwiftLint,OC语言对应的静态分析工具有Infer和OCLitn。以下会是对各个静态分析工具的安装和使用做一个介绍。

SwiftLint

cfea59e5851e65f1d764e75d44b56eb4.png对于Swift项目的静态分析可以使用SwiftLint。SwiftLint 是一个用于强制检查 Swift 代码风格和规定的一个工具。它的实现是 Hook 了 Clang 和 SourceKit 从而能够使用 AST 来表示源代码文件的更多精确结果。Clange我们了解了,那SourceKit是干什么用的?

SourceKit包含在Swift项目的主仓库,它是一套工具集,支持Swift的大多数源代码操作特性:源代码解析、语法突出显示、排版、自动完成、跨语言头生成等工作。

安装

安装有两种方式,任选其一:

方式一:通过Homebrew

$ brew install swiftlint

这种是全局安装,各个应用都可以使用。

方式二:通过CocoaPods

pod 'SwiftLint', :configurations => ['Debug']

这种方式相当于把SwiftLint作为一个三方库集成进了项目,因为它只是调试工具,所以我们应该将其指定为仅Debug环境下生效。

集成进Xcode

我们需要在项目中的Build Phases,添加一个Run Script Phase。如果是通过homebrew安装的,你的脚本应该是这样的。

if which swiftlint >/dev/null; then
  swiftlint
else
  echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"
fi

如果是通过cocoapods安装的,你的脚本应该是这样的:

"${PODS_ROOT}/SwiftLint/swiftlint"
324afd34b9826784781ea733f29c971f.png

运行SwiftLint

键入CMD + B编译项目,在编译完后会运行我们刚才加入的脚本,之后我们就能看到项目中大片的警告信息。有时候build信息并不能填入项目代码中,我们可以在编译的log日志里查看。

fe0d5c481e99b910167d5acc1c308758.png

定制

SwiftLint规则太多了,如果我们不想执行某一规则,或者想要滤掉对Pods库的分析,我们可以对SwfitLint进行配置。

在项目根目录新建一个.swiftlint.yml文件,然后填入如下内容:

disabled_rules: # rule identifiers to exclude from running
  - colon
  - trailing_whitespace
  - vertical_whitespace
  - function_body_length
opt_in_rules: # some rules are only opt-in
  - empty_count
  # Find all the available rules by running:
  # swiftlint rules
included: # paths to include during linting. `--path` is ignored if present.
  - Source
excluded: # paths to ignore during linting. Takes precedence over `included`.
  - Carthage
  - Pods
  - Source/ExcludedFolder
  - Source/ExcludedFile.swift
  - Source/*/ExcludedFile.swift # Exclude files with a wildcard
analyzer_rules: # Rules run by `swiftlint analyze` (experimental)
  - explicit_self

# configurable rules can be customized from this configuration file
# binary rules can set their severity level
force_cast: warning # implicitly
force_try:
  severity: warning # explicitly
# rules that have both warning and error levels, can set just the warning level
# implicitly
line_length: 110
# they can set both implicitly with an array
type_body_length:
  - 300 # warning
  - 400 # error
# or they can set both explicitly
file_length:
  warning: 500
  error: 1200
# naming rules can set warnings/errors for min_length and max_length
# additionally they can set excluded names
type_name:
  min_length: 4 # only warning
  max_length: # warning and error
    warning: 40
    error: 50
  excluded: iPhone # excluded via string
  allowed_symbols: ["_"] # these are allowed in type names
identifier_name:
  min_length: # only min_length
    error: 4 # only error
  excluded: # excluded via string array
    - id
    - URL
    - GlobalAPIKey
reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, junit, html, emoji, sonarqube, markdown)

一条rules提示如下,其对应的rules名就是function_body_length

! Function Body Length Violation: Function body should span 40 lines or less excluding comments and whitespace: currently spans 43 lines (function_body_length)

disabled_rules下填入我们不想遵循的规则。

excluded设置我们想跳过检查的目录,Carthage、Pod、SubModule这些一般可以过滤掉。

其他的一些像是文件长度(file_length),类型名长度(type_name),我们可以通过设置具体的数值来调节。

另外SwiftLint也支持自定义规则,我们可以根据自己的需求,定义自己的rule

生成报告

如果我们想将此次分析生成一份报告,也是可以的(该命令是通过homebrew安装的swiftlint):

# reporter type (xcode, json, csv, checkstyle, junit, html, emoji, sonarqube, markdown)
$ swiftlint lint --reporter html > swiftlint.html
b4f292d236856919d8f718e8e896e28f.png

xcodebuild

xcodebuild是xcode内置的编译命令,我们可以用它来编译打包我们的iOS项目,接下来介绍的Infer和OCLint都是基于xcodebuild的编译产物进行分析的,所以有必要简单介绍一下它。

一般编译一个项目,我们需要指定项目名,configuration,scheme,sdk等信息以下是几个简单的命令及说明。

# 不带pod的项目,target名为TargetName,在Debug下,指定模拟器sdk环境进行编译
xcodebuild -target TargetName -configuration Debug -sdk iphonesimulator
# 带pod的项目,workspace名为TargetName.xcworkspace,在Release下,scheme为TargetName,指定真机环境进行编译。不指定模拟器环境会验证证书
xcodebuild -workspace WorkspaceName.xcworkspace -scheme SchemeName Release
# 清楚项目的编译产物
xcodebuild -workspace WorkspaceName.xcworkspace -scheme SchemeName Release clean

之后对xcodebuild命令的使用都需要将这些参数替换为自己项目的参数。

Infer

1502ee8da7ac338f404dcbe0ef1075b5.pngInfer是Facebook开发的针对C、OC、Java语言的静态分析工具,它同时支持对iOS和Android应用的分析。对于Facebook内部的应用像是 Messenger、Instagram 和其他一些应用均是有它进行静态分析的。它主要检测隐含的问题,主要包括以下几条:

  • 资源泄露,内存泄露
  • 变量和参数的非空检测
  • 循环引用
  • 过早的nil操作

暂不支持自定义规则。

安装及使用

$ brew install infer

运行infer

$ cd projectDir
# 跳过对Pods的分析
$ infer run --skip-analysis-in-path Pods -- xcodebuild -workspace "Project.xcworkspace" -scheme "Scheme" -configuration Debug -sdk iphonesimulator

我们会得到一个infer-out的文件夹,里面是各种代码分析的文件,有txt,json等文件格式,当这样不方便查看,我们可以将其转成html格式:

$ infer explore --html
026cb3b1bf1ed9496d323ca3c7b6f8ff.png

点击trace,我们会看到该问题代码的上下文。

因为Infer默认是增量编译,只会分析变动的代码,如果我们想整体编译的话,需要clean一下项目:

$ xcodebuild -workspace "Project.xcworkspace" -scheme "Scheme" -configuration Debug -sdk iphonesimulator clean

再次运行Infer去编译。

$ infer run --skip-analysis-in-path Pods -- xcodebuild -workspace "Project.xcworkspace" -scheme "Scheme" -configuration Debug -sdk iphonesimulator

Infer的大致原理

Infer的静态分析主要分两个阶段:

1、捕获阶段

Infer 捕获编译命令,将文件翻译成 Infer 内部的中间语言。

这种翻译和编译类似,Infer 从编译过程获取信息,并进行翻译。这就是我们调用 Infer 时带上一个编译命令的原因了,比如: infer -- clang -c file.c, infer -- javac File.java。结果就是文件照常编译,同时被 Infer 翻译成中间语言,留作第二阶段处理。特别注意的就是,如果没有文件被编译,那么也没有任何文件会被分析。

Infer 把中间文件存储在结果文件夹中,一般来说,这个文件夹会在运行 infer 的目录下创建,命名是 infer-out/

2、分析阶段

在分析阶段,Infer 分析 infer-out/ 下的所有文件。分析时,会单独分析每个方法和函数。

在分析一个函数的时候,如果发现错误,将会停止分析,但这不影响其他函数的继续分析。

所以你在检查问题的时候,修复输出的错误之后,需要继续运行 Infer 进行检查,知道确认所有问题都已经修复。

错误除了会显示在标准输出之外,还会输出到文件 infer-out/bug.txt 中,我们过滤这些问题,仅显示最有可能存在的。

在结果文件夹中(infer-out),同时还有一个 csv 文件 report.csv,这里包含了所有 Infer 产生的信息,包括:错误,警告和信息。

OCLint

OCLint是基于Clange Tooling编写的库,它支持扩展,检测的范围比Infer要大。不光是隐藏bug,一些代码规范性的问题,例如命名和函数复杂度也均在检测范围之内。

安装OCLint

OCLint一般通过Homebrew安装

$ brew tap oclint/formulae   
$ brew install oclint

通过Hombrew安装的版本为0.13。

$ oclint --version
LLVM (http://llvm.org/):
  LLVM version 5.0.0svn-r313528
  Optimized build.
  Default target: x86_64-apple-darwin19.0.0
  Host CPU: skylake

OCLint (http://oclint.org/):
  OCLint version 0.13.
  Built Sep 18 2017 (08:58:40).

我分别用Xcode11在两个项目上运行过OCLint,一个实例项目可以正常运行,另一个复杂的项目却运行失败,报如下错误:

1 error generated
1 error generated
...
oclint: error: cannot open report output file ..../onlintReport.html

我并不清楚原因,如果你想试试0.13能否使用的话,直接跳到安装xcpretty。如果你也遇到了这个问题,可以回来安装oclint0.15版本。

OCLint0.15

我在oclint issuse #547这里找到了这个问题和对应的解决方案。

我们需要更新oclint至0.15版本。brew上的最新版本是0.13,github上的最新版本是0.15。我下载github上的release0.15版本,但是这个包并不是编译过的,不清楚是不是官方自己搞错了,只能手动编译了。因为编译要下载llvm和clange,这两个包较大,所以我将编译过后的包直接传到了这里CodeChecker。

如果不关心编译过程,可以下载编译好的包,跳到设置环境变量那一步。

编译OCLint

1、安装CMake和Ninja这两个编译工具

$ brew install cmake ninja

2、clone OCLint项目

$ git clone https://github.com/oclint/oclint

3、进入oclint-scripts目录,执行make命令

$ ./make

成功之后会出现build文件夹,里面有个oclint-release就是编译成功的oclint工具。

设置oclint工具的环境变量

设置环境变量的目的是为了我们能够快捷访问。然后我们需要配置PATH环境变量,注意OCLint_PATH的路径为你存放oclint-release的路径。将其添加到.zshrc,或者.bash_profile文件末尾:

OCLint_PATH=/Users/zhangferry/oclint/build/oclint-release
export PATH=$OCLint_PATH/bin:$PATH

执行source .zshrc,刷新环境变量,然后验证oclint是否安装成功:

$ oclint --version
OCLint (http://oclint.org/):
OCLint version 0.15.
Built May 19 2020 (11:48:49).

出现这个介绍就说明我们已经完成了安装。

安装xcpretty

xcpretty是一个格式化xcodebuild输出内容的脚本工具,oclint的解析依赖于它的输出。它的安装方式为:

$ gem install xcpretty

OCLint的使用

在使用OCLint之前还需要一些准备工作,需要将编译项COMPILER_INDEX_STORE_ENABLE设置为NO。

  • 将 Project 和 Targets 中 Building Settings 下的 COMPILER_INDEX_STORE_ENABLE 设置为 NO
  • 在 podfile 中 target 'target' do 前面添加下面的脚本,将各个pod的编译配置也改为此选项
post_install do |installer|
  installer.pods_project.targets.each do |target|
      target.build_configurations.each do |config|
          config.build_settings['COMPILER_INDEX_STORE_ENABLE'] = "NO"
      end
  end
end

使用方式

1、进入项目根目录,运行如下脚本:

$ xcodebuild -workspace ProjectName.xcworkspace -scheme ProjectScheme -configuration Debug -sdk iphonesimulator | xcpretty -r json-compilation-database -o compile_commands.json

会将xcodebuild编译过程中的一些信息记录成一个文件compile_commands.json,如果我们在项目根目录看到了该文件,且里面是有内容的,证明我们完成了第一步。

2、我们将这个json文件转成方便查看的html,过滤掉对Pods文件的分析,为了防止行数上限,我们加上行数的限制:

$ oclint-json-compilation-database -e Pods -- -report-type html -o oclintReport.html -rc LONG_LINE=9999 -max-priority-1=9999 -max-priority-2=9999 -max-priority-3=9999

最终会产生一个oclintReport.html文件。

5aee21b1c3a2195aecd30a1cb72cbe49.png

OCLint支持自定义规则,因为其本身规则已经很丰富了,自定义规则的需求应该很小,也就没有尝试。

封装脚本

OCLint跟Infer一样都是通过运行几个脚本语言进行执行的,我们可以将这几个命令封装成一个脚本文件,以OCLint为例,Infer也类似:

#!/bin/bash
# mark sure you had install the oclint and xcpretty

# You need to replace these values with your own project configuration
workspace_name="WorkSpaceName.xcworkspace"
scheme_name="SchemeName"

# remove history
rm compile_commands.json
rm oclint_result.xml
# clean project
# -sdk iphonesimulator means run simulator
xcodebuild -workspace $workspace_name -scheme $scheme_name -configuration Debug -sdk iphonesimulator clean || (echo "command failed"; exit 1);

# export compile_commands.json
xcodebuild -workspace $workspace_name -scheme $scheme_name -configuration Debug -sdk iphonesimulator \
| xcpretty -r json-compilation-database -o compile_commands.json \
|| (echo "command failed"; exit 1);

# export report html
# you can run `oclint -help` to see all USAGE
oclint-json-compilation-database -e Pods -- -report-type html -o oclintReport.html \
-disable-rule ShortVariableName \
-rc LONG_LINE=1000 \
|| (echo "command failed"; exit 1);

open -a "/Applications/Safari.app" oclintReport.html

oclint-json-compilation-database命令的几个参数说明:

-e需要忽略分析的文件,这些文件的警告不会出现在报告中

-rc需要覆盖的规则的阀值,这里可以自定义项目的阀值,默认阀值

-enable-rule支持的规则,默认是oclint提供的都支持,可以组合-disable-rule来过滤掉一些规则规则列表

-disable-rule需要忽略的规则,根据项目需求设置

在Xcode中使用OCLint

因为OCLint提供了xcode格式的输出样式,所以我们可以将它作为一个脚本放在Xcode中。

1、在项目的 TARGETS 下面,点击下方的 "+" ,选择 cross-platform 下面的 Aggregate。输入名字,这里命名为 OCLint

3b0718ef8fe1c3a146624445078bc151.png
new_target

2、选中该Target,进入Build Phases,添加Run Script,写入下面脚本:

# Type a script or drag a script file from your workspace to insert its path.
# 内置变量
cd ${SRCROOT}
xcodebuild clean 
xcodebuild | xcpretty -r json-compilation-database
oclint-json-compilation-database -e Pods -- -report-type xcode

可以看出该脚本跟上面的脚本一样,只不过 将oclint-json-compilation-database命令的-report-typehtml改为了xcode。而OCLint作为一个target本身就运行在特定的环境下,所以xcodebuild可以省去配置参数。

3、通过CMD + B我们编译一下项目,执行脚本任务,会得到能够定位到代码的warning信息:

8f1ed70568a24f500db3d2a772b8273c.png
xcode_warning

总结

以下是对这几种静态分析方案的对比,我们可以根据需求选择适合自己的静态分析方案。

SwiftLintInferOCLint
支持语言SwiftC、C++、OC、JavaC、C++、OC
易用性简单较简单较简单
能否集成进Xcode可以不能集成进xcode可以
自带规则丰富度较多,包含代码规范相对较少,主要检测潜在问题较多,包含代码规范
规则扩展性可以不可以可以

参考

OCLint 实现 Code Review - 给你的代码提提质量

Using OCLint in Xcode

Infer 的工作机制

LLVM & Clang 入门

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/567329.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql 查看表是否存在_MySQL优化篇二

单表优化最佳左前缀原则为,保持索引的定义和使用顺序的一致性将含In的范围查询,放到where条件语句的最后。索引需要逐步优化两表优化小表驱动大表避免索引失效的一些原则:复合索引,不要跨列或无序使用(最佳左前缀&…

java http请求_如何设置Fiddler来拦截Java代码发送HTTP请求,进行各种问题排查

我们使用Java的RestTemplate或者Apache的HTTPClient编程的时候,经常遇到需要跟踪Java代码发送的HTTP请求明细的情况。和javascript代码在浏览器里发送请求可以通过Chrome开发者工具方便地跟踪一样,对于Java代码发送的网络请求,我们也可以使用…

mysql自增id用完了_MySQL 自增 ID 用完了怎么办?

MySQL 自增 ID 用完了怎么办?在MySQL中有很多类型的自增ID,每个自增ID都设置了初始值,然后按照一定的步长增加,只要定义了字节长度,那么就会有上限,如果达到上限再次添加,则会报主键冲突错误&am…

山洪沟防洪治理工程技术规范_幸福沟水库一期工程完工丨尚志城市供水、防洪、农业灌溉皆升级...

日前,尚志市幸福沟水库一期工程建设完成,已蓄水试运行。幸福沟水库工程是列入黑龙江省“十三五”规划的重点水利项目,是哈市重点推进的两座中型水库之一,也是尚志市一项打基础、利长远、惠民生的重大战略民生工程。幸福沟水库位于…

子类怎么继承父类方法中的变量_JavaOOP_04 封装 继承

一、封装概念:隐藏程序内部的具体实现细节,对外提供接口,从而提高程序的安全性。高内聚,低耦合。使用封装的步骤:1、属性私有化,使用private访问修饰符进行修饰2、对外提供 setter/getter 方法 setter设置值…

mysql 社区版密码如何修改_如何用优雅的方法修改MySQL root密码

搭噶好,我系小编,我好久没发文章了啊,今天发文章的原因是,我竟然忘了我的MySQL root密码。。本来想找RAKsmart客服直接重置啥的,后来想想还是自己搞吧,还能长知识。然后,我发现一个事情&#xf…

中countif函数_Count系列函数-Count、Counta、Countblank、Countif、Countifs

统计函数在我们的日常工作中也会经常使用。常见的有Count、Counta、Countblank、Countif、Countifs五个。Count用于统计数据类型的单元格个数,Counta用于统计非空单元格的个数,Countblank用于统计空单元格的个数,而Countif、Countifs用于条件…

labview实例_手把手以实例教你学LabVIEW编程,条件结构编程方法

LabVIEW又称为G语言,简单易学、形象直观,采用图形化的编程方式,是专为测试、测量和控制应用而设计的系统工程软件。在上篇文章(请参考:顺序结构编程)中已经讲了顺序结构的编程方法,下面通过具体例子演示条件结构的编程…

用递归与分治策略求解网球循环赛日程表_算法设计:分治法(比赛日程安排)...

一、算法思路1、思路分治算法的思想是:对于一个规模位N的问题,若该问题可以容易解决(比如规模N较小),则直接解决,否则将其分解为M个规模较小的子问题,这些子问题互相独立,并且与原问题形式相同,…

聚类算法 距离矩阵_谱聚类

比起传统的K-means算法,谱聚类对数据分布的适应性更强,计算量也要小很多。1. 谱聚类概述谱聚类是从图论中演化出来,主要思想是吧所有的数据看作空间中的点,这些点之间可以用边连接起来。距离较远的两个点之间的边权重值较低&#…

vlan划分不能上网_VLAN工作原理

什么是VLANVLAN(Virtual LAN),翻译成中文是“虚拟局域网”。可以看做是在一个物理局域网络上搭建出几个逻辑上分离的几个局域网。举个例子来说,如果一个交换机划分为两个VLAN,则相当于这台交换机逻辑上划分为两个交换机。VLAN的一个简单直观说…

控制for each循环次数_CCF CSP编程题解201312-1:出现次数最多的数

试题编号:201312-1试题名称:出现次数最多的数时间限制:1.0s内存限制:256.0MB问题描述:给定n个正整数,找出它们中出现次数最多的数。如果这样的数有多个,请输出其中最小的一个。输入格式:输入的第一行只有一…

旋流式沉砂池计算_旋流沉砂池设计方法

旋流沉砂池设计接口条件和主要参数设计旋流沉砂池前要确认的接口条件和信息包括:地质、气候等基本设计条件;可用地尺寸及在总图的位置坐标;地坪标高,上下游水位或范围,冻土层高度,管道覆土小深度要求&#…

parallelstream启动的线程数_高并发与多线程网络学习笔记(三)线程组和线程池

线程组线程组的作用是:可以批量管理线程或线程组对象,有效地对线程或线程组对象进行组织。构造函数ThreadGroup(String name)//默认parent为当前线程组 ThreadGroup(ThreadGroup parent, String name)具体方法//评估当前活跃的线程数,包括当前group和子g…

pytorch l2正则化_吴恩达深度学习 编程作业六 正则化(2)

推荐守门员应该将球踢到哪个位置,才能让自己的队员用头击中。1.无正则化模型判别是否有正则化与调用其他计算函数。准确率:0.948/0.915明显过拟合overfiting了。2.L2正则化公式如下,在原有cost函数基础上增加L2项,L2为参数w的均方…

vs中工具箱代表的意思_“日”除了代表太阳,其实还有这种意思,特别是出现在这些词语中的“日”...

对于“日”来说,大家都并不陌生,对于日的成语更是多得数不胜数,今天小编将日字成语进行了一个总结,需要用到的朋友们可以收藏!一:以“日”开头的成语,大多数形容的是时间。日积月累、日久天长、…

mysql or走索引吗_加了索引,mysql查询就一定会用吗?

小白白跑去鹅厂面试,面试官提出了一个很实际的问题: mysql增加索引,那些情况会失效呢?谈一下实际工作中遇到的情况。我们的小白白又抛出了白氏秘籍:用不用索引,找DBA小姐姐!啊?这是你…

基本农田卫星地图查询_如何基于西安80坐标查询定位

1. 概述水经注软件除了可以轻松下载无水印Google Earth卫星影像、有明确拍摄日期的历史影像、地方高清天地图、百度高德大字体打印地图,且可按1万/5千等国家标准图幅下载,下载含高度的全国矢量建筑、全国乡镇及街区行政区划、地名点、高速铁路网、公交路…

tcp的无延时发送_高并发架构的TCP知识介绍

这是关于高并发架构网络协议基础知识的第二篇,编程路上的基础心法!做为一个有追求的程序员,不能只满足增删改查,我们要对系统全方面无死角掌控。掌握了这些基本的网络知识后,相信一方面日常排错中会事半功倍&#xff0…

如何对一个变量数据进行正则判定_生存分析数据中的BuckleyJamesMultipleRegression Model...

一、模型简介目前,生存分析领域,最常用的是Cox比例风险回归模型,该模型具有良好的特性,不仅可以分析各种自变量对生存时间的影响,而且对基准风险分布不作任何要求(半参数模型)。Cox模型使用时要满足一定的条件&#xf…