OCLint内置了72条检测规则,如果对这些规则不加以自定义的话很可能会出现上万条不规范的内容,代码是为了实现功能,规范也一定要注意,但是不能本末倒置,我找了一个比较老的工程进行了测试,输出的报告如下:
这个工程的代码量其实还算不上很大,已经出现了这么多不符合规则的内容,粗略整理了一下,大概有三十多条检测规则是代码中比较常用的。
OCLint常见规则
规则名称 | 自定义规则所对应KEY值 | 含义 | 备注 |
---|---|---|---|
long variable name | LongVariableName | 过长的变量名称 | |
long line | LongLine | 一行代码包含过多的字符 | |
empty else block | EmptyElseBlock | else语句中是空的 | |
unused method parameter | UnusedMethodParameter | 未使用的参数 | |
prefer early exits and continue | PreferEarlyExit | 如果存在退出语句,让满足退出条件的选项尽早退出 | 代码示例 |
empty catch statement | EmptyCatchStatement | try-catchy语句中catch代码块为空 | |
too many methods | TooManyMethods | 一个类中包含了过多的方法 | |
collapsible if statements | CollapsibleIfStatements | 如果可以尽量合并if语句 | 代码示例 |
ivar assignment outside accessors or init | AssignIvarOutsideAccessors | 某些成员变量的初始化不在get,set,init方法中 | 代码示例 |
redundant nil check | RedundantNilCheck | 非必要的nil检查 | 代码示例 |
high ncss method | HighNcssMethod | “非注释的源语句”过多,其实说的就是一个方法不包含注释,有效的代码量过大 | 代码示例 |
high npath complexity | HighNpathComplexity | 一个方法中可能执行的路径总和过多 | 代码示例 |
long method | LongMethod | 一个方法过于庞大,做了太多事情 | |
high cyclomatic complexity | HighCyclomaticComplexity | 代码圈度过高,包含了很多if else while等语句 | 代码示例 |
empty if statement | EmptyIfStatement | if语句内容为空 | |
useless parentheses | UselessParentheses | 无用的括号 | 代码示例 |
redundant conditional operator | RedundantConditionalOperator | 冗余的条件判断 | 代码示例 |
redundant local variable | RedundantLocalVariable | 多余的变量创建 | 代码示例 |
unnecessary else statement | UnnecessaryElseStatement | 不必要的else判断 | 代码示例 |
unused local variable | UnusedLocalVariable | 定义了一个变量但未使用 | 代码示例 |
dead code | DeadCode | 顾名思义,死代码,所写的逻辑不影响最终的执行结果或者代码不会被执行 | 代码示例 |
inverted logic | InvertedLogic | 逆逻辑,代码的逻辑很难被理解 | 代码示例 |
constant if expression | ConstantIfExpression | if语句的判断总是true或者false | 代码示例 |
too few branches in switch statement | TooFewBranchesInSwitchStatement | switch语句的分支太少 | 代码示例 |
missing default in switch statements | MissingDefaultStatement | switch语句却少default | 代码示例 |
bitwise operator in conditional | BitwiseOperatorInConditional | 条件语句中使用了位运算,位运算很高效,但有时难以理解 | 代码示例 |
unnecessary default statement in covered switch statement | UnnecessaryDefaultStatement | 不必要的default,switch已经罗列出所有的case分支 | 代码示例 |
redundant if statement | RedundantIfStatement | 没必要的条件判断 | 代码示例 |
long class | LongClass | 太大的类,超过了1000行代码 | 代码示例 |
deep nested block | DeepNestedBlock | 太深的嵌套 | 代码示例 |
missing break in switch statement | MissingBreakInSwitchStatement | switch语句缺少break | 代码示例 |
constant conditional operator | ConstantConditionalOperator | 常量条件运算符,结果总为true或者false | 代码示例 |
broken oddness check | BrokenOddnessCheck | 奇数检查不正确,出现这个问题大概率是使用%运算进行判断 | 代码示例 |
use number literal | UseNumberLiteral | 推荐直接使用数字来进行初始化,而不是调用类方法 | 代码示例 |
short variable name | ShortVariableName | 过短的变量名称 | 代码示例 |
use container literal | UseContainerLiteral | 推荐使用容器语法初始化数组字典等 | 代码示例 |
parameter reassignment | ParameterReassignment | 参数被重新定义了,这是不标准的写法 | 代码示例 |
use object subscripting | UseObjectSubscripting | 不推荐使用下标在数组或者字典中取值 | 代码示例 |
定义具体检测规则
上面所列出来的规则是可以根据我们的项目需要进行自定义的,比如设置每行代码最多200个字符。
-rc=LONG_LINE=200
设置变量名称最长40个字符
-rc=LONG_VARIABLE_NAME=40
忽略某些检测规则
-disable-rule ShortVariableName
-disable-rule UseContainerLiteral
-disable-rule ParameterReassignment
-disable-rule UseObjectSubscripting
完整的检测语句如下:
oclint-json-compilation-database -e Pods -e Applications -- -extra-arg=-Wno-everything -report-type html -o oclintReport.html \-rc=LONG_LINE=200 \-rc=LONG_VARIABLE_NAME=40 \-disable-rule ShortVariableName \-disable-rule UseContainerLiteral \-disable-rule ParameterReassignment \-disable-rule UseObjectSubscripting
生成报告
可以选择输出html
格式的报告,直接使用浏览器就可以查看结果。
结果查看起来还是非常方便的,会定位到具体的类以及对应的行,还会将具体的错误类别展示出来。有一个点要注意,在生成编译文件的时候要加上一句
GCC_PRECOMPILE_PREFIX_HEADER=YES
,预先编译prefix文件,不然最终展示出来的报告会出现很多编译问题,完整编译语句如下:
xcodebuild clean build -scheme <your_scheme> -workspace <your_workspace>.xcworkspace -configuration Debug GCC_PRECOMPILE_PREFIX_HEADER=YES COMPILER_INDEX_STORE_ENABLE=NO -sdk iphoneos16.2 | xcpretty -r json-compilation-database -o compile_commands.json
脚本文件
每次都使用命令行进行代码的扫描,其实是很费劲的一件事情,可以直接使用脚本文件进行检测。
#!/bin/bash
COLOR_ERR="\033[1;31m" #出错提示
COLOR_SUCC="\033[0;32m" #成功提示
COLOR_QS="\033[1;37m" #问题颜色
COLOR_AW="\033[0;37m" #答案提示
COLOR_END="\033[1;34m" #颜色结束符
# 寻找项目的 ProjectName
function searchProjectName () {
# maxdepth 查找文件夹的深度
find . -maxdepth 1 -name "*.xcodeproj"
}
function oclintForProject () {
# 预先检测所需的安装包是否存在
if which xcodebuild 2>/dev/null; then
echo 'xcodebuild exist'
else
echo 'xcodebuild 未安装,请安装Xcode'
fi
if which oclint 2>/dev/null; then
echo 'oclint exist'
else
export PATH="/Users/imac0823/Documents/Tools/oclint/bin:$PATH"
source ~/.zshrc
fi
if which xcpretty 2>/dev/null; then
echo 'xcpretty exist'
else
echo 'xcpretty 未安装,请安装xcpretty'
fi
# 指定编码
export LANG="zh_CN.UTF-8"
export LC_COLLATE="zh_CN.UTF-8"
export LC_CTYPE="zh_CN.UTF-8"
export LC_MESSAGES="zh_CN.UTF-8"
export LC_MONETARY="zh_CN.UTF-8"
export LC_NUMERIC="zh_CN.UTF-8"
export LC_TIME="zh_CN.UTF-8"
export xcpretty=/usr/local/bin/xcpretty # xcpretty 的安装位置可以在终端用 which xcpretty找到
searchFunctionName=`searchProjectName`
path=${searchFunctionName}
# 字符串替换函数。//表示全局替换 /表示匹配到的第一个结果替换。
path=${path//.\//} # ./BridgeLabiPhone.xcodeproj -> BridgeLabiPhone.xcodeproj
path=${path//.xcodeproj/} # BridgeLabiPhone.xcodeproj -> BridgeLabiPhone
myworkspace=$path".xcworkspace" # workspace名字
myscheme=$path # scheme名字
# 清除上次编译数据
DIR=~/Library/Developer/Xcode/DerivedData/
echo -e $COLOR_SUCC'🚀🚀🚀🚀🚀清除上次编译数据🚀🚀🚀🚀🚀'$COLOR_SUCC
rm -r -- "$DIR"*
# # 生成编译数据
xcodebuild GCC_PRECOMPILE_PREFIX_HEADER=YES COMPILER_INDEX_STORE_ENABLE=NO OTHER_CFLAGS="-DNS_FORMAT_ARGUMENT(A)= -D_Nullable_result=_Nullable" clean build -scheme $myscheme -workspace $myworkspace -configuration Debug -sdk iphoneos16.2 |tee xcodebuild.log|xcpretty -r json-compilation-database -o compile_commands.json
if [ -f ./compile_commands.json ]; then
echo -e $COLOR_SUCC'🚀🚀🚀🚀🚀xcpretty编译数据生成完毕🚀🚀🚀🚀🚀'$COLOR_SUCC
else
echo -e $COLOR_ERR'❌❌❌xcpretty编译数据生成失败❌❌❌'$COLOR_ERR
return -1
fi
if [ -f ./compile_commands.json ]; then
echo -e $COLOR_SUCC'🚀🚀🚀🚀🚀xcpretty编译数据生成完毕🚀🚀🚀🚀🚀'$COLOR_SUCC
else
echo -e $COLOR_ERR'❌❌❌xcpretty编译数据生成失败❌❌❌'$COLOR_ERR
return -1
fi
echo -e $COLOR_SUCC'🚀🚀🚀🚀🚀OCLint代码分析开始🚀🚀🚀🚀🚀'$COLOR_SUCC
# 生成报表
oclint-json-compilation-database -e Pods -e Applications -- -extra-arg=-Wno-everything -report-type html -o oclintReport.html \-rc=LONG_LINE=200 \-rc=LONG_VARIABLE_NAME=40 \-disable-rule ShortVariableName \-disable-rule UseContainerLiteral \-disable-rule ParameterReassignment \-disable-rule UseObjectSubscripting \-disable-rule AssignIvarOutsideAccessors \-disable-rule UnusedMethodParameter
if [ -f ./oclintReport.html ]; then
echo -e $COLOR_SUCC'🚀🚀🚀🚀🚀oclint分析数据生成完毕🚀🚀🚀🚀🚀'$COLOR_SUCC
else
echo -e $COLOR_ERR'❌❌❌oclint分析数据生成失败❌❌❌'$COLOR_ERR
return -1
fi
}
oclintForProject $1
OCLint的不足之处
OCLint可以检测出来许许多多代码不规范的地方,这些不规范会影响代码的可读性问题,不易于维护,时间长了会造成很多技术债,但是更深层次的问题比如资源泄漏,内存泄漏这类问题OCLint是无法检测出来的,需要借助其他工具,比如Facebook的Infer,这个放到下一篇再去分享。