OCLint內(nèi)置了72條檢測規(guī)則,如果對這些規(guī)則不加以自定義的話很可能會出現(xiàn)上萬條不規(guī)范的內(nèi)容,代碼是為了實(shí)現(xiàn)功能,規(guī)范也一定要注意,但是不能本末倒置,我找了一個比較老的工程進(jìn)行了測試,輸出的報(bào)告如下:

這個工程的代碼量其實(shí)還算不上很大,已經(jīng)出現(xiàn)了這么多不符合規(guī)則的內(nèi)容,粗略整理了一下,大概有三十多條檢測規(guī)則是代碼中比較常用的。
OCLint常見規(guī)則
| 規(guī)則名稱 | 自定義規(guī)則所對應(yīng)KEY值 | 含義 | 備注 |
|---|---|---|---|
| long variable name | LongVariableName | 過長的變量名稱 | |
| long line | LongLine | 一行代碼包含過多的字符 | |
| empty else block | EmptyElseBlock | else語句中是空的 | |
| unused method parameter | UnusedMethodParameter | 未使用的參數(shù) | |
| prefer early exits and continue | PreferEarlyExit | 如果存在退出語句,讓滿足退出條件的選項(xiàng)盡早退出 | 代碼示例 |
| 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 | “非注釋的源語句”過多,其實(shí)說的就是一個方法不包含注釋,有效的代碼量過大 | 代碼示例 |
| high npath complexity | HighNpathComplexity | 一個方法中可能執(zhí)行的路徑總和過多 | 代碼示例 |
| long method | LongMethod | 一個方法過于龐大,做了太多事情 | |
| high cyclomatic complexity | HighCyclomaticComplexity | 代碼圈度過高,包含了很多if else while等語句 | 代碼示例 |
| empty if statement | EmptyIfStatement | if語句內(nèi)容為空 | |
| useless parentheses | UselessParentheses | 無用的括號 | 代碼示例 |
| redundant conditional operator | RedundantConditionalOperator | 冗余的條件判斷 | 代碼示例 |
| redundant local variable | RedundantLocalVariable | 多余的變量創(chuàng)建 | 代碼示例 |
| unnecessary else statement | UnnecessaryElseStatement | 不必要的else判斷 | 代碼示例 |
| unused local variable | UnusedLocalVariable | 定義了一個變量但未使用 | 代碼示例 |
| dead code | DeadCode | 顧名思義,死代碼,所寫的邏輯不影響最終的執(zhí)行結(jié)果或者代碼不會被執(zhí)行 | 代碼示例 |
| 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 | 條件語句中使用了位運(yùn)算,位運(yùn)算很高效,但有時難以理解 | 代碼示例 |
| unnecessary default statement in covered switch statement | UnnecessaryDefaultStatement | 不必要的default,switch已經(jīng)羅列出所有的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 | 常量條件運(yùn)算符,結(jié)果總為true或者false | 代碼示例 |
| broken oddness check | BrokenOddnessCheck | 奇數(shù)檢查不正確,出現(xiàn)這個問題大概率是使用%運(yùn)算進(jìn)行判斷 | 代碼示例 |
| use number literal | UseNumberLiteral | 推薦直接使用數(shù)字來進(jìn)行初始化,而不是調(diào)用類方法 | 代碼示例 |
| short variable name | ShortVariableName | 過短的變量名稱 | 代碼示例 |
| use container literal | UseContainerLiteral | 推薦使用容器語法初始化數(shù)組字典等 | 代碼示例 |
| parameter reassignment | ParameterReassignment | 參數(shù)被重新定義了,這是不標(biāo)準(zhǔn)的寫法 | 代碼示例 |
| use object subscripting | UseObjectSubscripting | 不推薦使用下標(biāo)在數(shù)組或者字典中取值 | 代碼示例 |
定義具體檢測規(guī)則
上面所列出來的規(guī)則是可以根據(jù)我們的項(xiàng)目需要進(jìn)行自定義的,比如設(shè)置每行代碼最多200個字符。
-rc=LONG_LINE=200
設(shè)置變量名稱最長40個字符
-rc=LONG_VARIABLE_NAME=40
忽略某些檢測規(guī)則
-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
生成報(bào)告
可以選擇輸出html格式的報(bào)告,直接使用瀏覽器就可以查看結(jié)果。

結(jié)果查看起來還是非常方便的,會定位到具體的類以及對應(yīng)的行,還會將具體的錯誤類別展示出來。有一個點(diǎn)要注意,在生成編譯文件的時候要加上一句
GCC_PRECOMPILE_PREFIX_HEADER=YES,預(yù)先編譯prefix文件,不然最終展示出來的報(bào)告會出現(xiàn)很多編譯問題,完整編譯語句如下:
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
腳本文件
每次都使用命令行進(jìn)行代碼的掃描,其實(shí)是很費(fèi)勁的一件事情,可以直接使用腳本文件進(jìn)行檢測。
#!/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" #顏色結(jié)束符
# 尋找項(xiàng)目的 ProjectName
function searchProjectName () {
# maxdepth 查找文件夾的深度
find . -maxdepth 1 -name "*.xcodeproj"
}
function oclintForProject () {
# 預(yù)先檢測所需的安裝包是否存在
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}
# 字符串替換函數(shù)。//表示全局替換 /表示匹配到的第一個結(jié)果替換。
path=${path//.\//} # ./BridgeLabiPhone.xcodeproj -> BridgeLabiPhone.xcodeproj
path=${path//.xcodeproj/} # BridgeLabiPhone.xcodeproj -> BridgeLabiPhone
myworkspace=$path".xcworkspace" # workspace名字
myscheme=$path # scheme名字
# 清除上次編譯數(shù)據(jù)
DIR=~/Library/Developer/Xcode/DerivedData/
echo -e $COLOR_SUCC'??????????清除上次編譯數(shù)據(jù)??????????'$COLOR_SUCC
rm -r -- "$DIR"*
# # 生成編譯數(shù)據(jù)
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編譯數(shù)據(jù)生成完畢??????????'$COLOR_SUCC
else
echo -e $COLOR_ERR'???xcpretty編譯數(shù)據(jù)生成失敗???'$COLOR_ERR
return -1
fi
if [ -f ./compile_commands.json ]; then
echo -e $COLOR_SUCC'??????????xcpretty編譯數(shù)據(jù)生成完畢??????????'$COLOR_SUCC
else
echo -e $COLOR_ERR'???xcpretty編譯數(shù)據(jù)生成失敗???'$COLOR_ERR
return -1
fi
echo -e $COLOR_SUCC'??????????OCLint代碼分析開始??????????'$COLOR_SUCC
# 生成報(bào)表
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分析數(shù)據(jù)生成完畢??????????'$COLOR_SUCC
else
echo -e $COLOR_ERR'???oclint分析數(shù)據(jù)生成失敗???'$COLOR_ERR
return -1
fi
}
oclintForProject $1
OCLint的不足之處
OCLint可以檢測出來許許多多代碼不規(guī)范的地方,這些不規(guī)范會影響代碼的可讀性問題,不易于維護(hù),時間長了會造成很多技術(shù)債,但是更深層次的問題比如資源泄漏,內(nèi)存泄漏這類問題OCLint是無法檢測出來的,需要借助其他工具,比如Facebook的Infer,這個放到下一篇再去分享。