OCLint內(nèi)置了72條檢測(cè)規(guī)則踩身,如果對(duì)這些規(guī)則不加以自定義的話(huà)很可能會(huì)出現(xiàn)上萬(wàn)條不規(guī)范的內(nèi)容稍浆,代碼是為了實(shí)現(xiàn)功能,規(guī)范也一定要注意试和,但是不能本末倒置讯泣,我找了一個(gè)比較老的工程進(jìn)行了測(cè)試,輸出的報(bào)告如下:
這個(gè)工程的代碼量其實(shí)還算不上很大阅悍,已經(jīng)出現(xiàn)了這么多不符合規(guī)則的內(nèi)容好渠,粗略整理了一下昨稼,大概有三十多條檢測(cè)規(guī)則是代碼中比較常用的。
OCLint常見(jiàn)規(guī)則
規(guī)則名稱(chēng) | 自定義規(guī)則所對(duì)應(yīng)KEY值 | 含義 | 備注 |
---|---|---|---|
long variable name | LongVariableName | 過(guò)長(zhǎng)的變量名稱(chēng) | |
long line | LongLine | 一行代碼包含過(guò)多的字符 | |
empty else block | EmptyElseBlock | else語(yǔ)句中是空的 | |
unused method parameter | UnusedMethodParameter | 未使用的參數(shù) | |
prefer early exits and continue | PreferEarlyExit | 如果存在退出語(yǔ)句拳锚,讓滿(mǎn)足退出條件的選項(xiàng)盡早退出 | 代碼示例 |
empty catch statement | EmptyCatchStatement | try-catchy語(yǔ)句中catch代碼塊為空 | |
too many methods | TooManyMethods | 一個(gè)類(lèi)中包含了過(guò)多的方法 | |
collapsible if statements | CollapsibleIfStatements | 如果可以盡量合并if語(yǔ)句 | 代碼示例 |
ivar assignment outside accessors or init | AssignIvarOutsideAccessors | 某些成員變量的初始化不在get假栓,set,init方法中 | 代碼示例 |
redundant nil check | RedundantNilCheck | 非必要的nil檢查 | 代碼示例 |
high ncss method | HighNcssMethod | “非注釋的源語(yǔ)句”過(guò)多霍掺,其實(shí)說(shuō)的就是一個(gè)方法不包含注釋?zhuān)行У拇a量過(guò)大 | 代碼示例 |
high npath complexity | HighNpathComplexity | 一個(gè)方法中可能執(zhí)行的路徑總和過(guò)多 | 代碼示例 |
long method | LongMethod | 一個(gè)方法過(guò)于龐大匾荆,做了太多事情 | |
high cyclomatic complexity | HighCyclomaticComplexity | 代碼圈度過(guò)高,包含了很多if else while等語(yǔ)句 | 代碼示例 |
empty if statement | EmptyIfStatement | if語(yǔ)句內(nèi)容為空 | |
useless parentheses | UselessParentheses | 無(wú)用的括號(hào) | 代碼示例 |
redundant conditional operator | RedundantConditionalOperator | 冗余的條件判斷 | 代碼示例 |
redundant local variable | RedundantLocalVariable | 多余的變量創(chuàng)建 | 代碼示例 |
unnecessary else statement | UnnecessaryElseStatement | 不必要的else判斷 | 代碼示例 |
unused local variable | UnusedLocalVariable | 定義了一個(gè)變量但未使用 | 代碼示例 |
dead code | DeadCode | 顧名思義杆烁,死代碼牙丽,所寫(xiě)的邏輯不影響最終的執(zhí)行結(jié)果或者代碼不會(huì)被執(zhí)行 | 代碼示例 |
inverted logic | InvertedLogic | 逆邏輯,代碼的邏輯很難被理解 | 代碼示例 |
constant if expression | ConstantIfExpression | if語(yǔ)句的判斷總是true或者false | 代碼示例 |
too few branches in switch statement | TooFewBranchesInSwitchStatement | switch語(yǔ)句的分支太少 | 代碼示例 |
missing default in switch statements | MissingDefaultStatement | switch語(yǔ)句卻少default | 代碼示例 |
bitwise operator in conditional | BitwiseOperatorInConditional | 條件語(yǔ)句中使用了位運(yùn)算兔魂,位運(yùn)算很高效烤芦,但有時(shí)難以理解 | 代碼示例 |
unnecessary default statement in covered switch statement | UnnecessaryDefaultStatement | 不必要的default,switch已經(jīng)羅列出所有的case分支 | 代碼示例 |
redundant if statement | RedundantIfStatement | 沒(méi)必要的條件判斷 | 代碼示例 |
long class | LongClass | 太大的類(lèi)入热,超過(guò)了1000行代碼 | 代碼示例 |
deep nested block | DeepNestedBlock | 太深的嵌套 | 代碼示例 |
missing break in switch statement | MissingBreakInSwitchStatement | switch語(yǔ)句缺少break | 代碼示例 |
constant conditional operator | ConstantConditionalOperator | 常量條件運(yùn)算符拍棕,結(jié)果總為true或者false | 代碼示例 |
broken oddness check | BrokenOddnessCheck | 奇數(shù)檢查不正確晓铆,出現(xiàn)這個(gè)問(wèn)題大概率是使用%運(yùn)算進(jìn)行判斷 | 代碼示例 |
use number literal | UseNumberLiteral | 推薦直接使用數(shù)字來(lái)進(jìn)行初始化勺良,而不是調(diào)用類(lèi)方法 | 代碼示例 |
short variable name | ShortVariableName | 過(guò)短的變量名稱(chēng) | 代碼示例 |
use container literal | UseContainerLiteral | 推薦使用容器語(yǔ)法初始化數(shù)組字典等 | 代碼示例 |
parameter reassignment | ParameterReassignment | 參數(shù)被重新定義了,這是不標(biāo)準(zhǔn)的寫(xiě)法 | 代碼示例 |
use object subscripting | UseObjectSubscripting | 不推薦使用下標(biāo)在數(shù)組或者字典中取值 | 代碼示例 |
定義具體檢測(cè)規(guī)則
上面所列出來(lái)的規(guī)則是可以根據(jù)我們的項(xiàng)目需要進(jìn)行自定義的骄噪,比如設(shè)置每行代碼最多200個(gè)字符尚困。
-rc=LONG_LINE=200
設(shè)置變量名稱(chēng)最長(zhǎng)40個(gè)字符
-rc=LONG_VARIABLE_NAME=40
忽略某些檢測(cè)規(guī)則
-disable-rule ShortVariableName
-disable-rule UseContainerLiteral
-disable-rule ParameterReassignment
-disable-rule UseObjectSubscripting
完整的檢測(cè)語(yǔ)句如下:
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é)果查看起來(lái)還是非常方便的事甜,會(huì)定位到具體的類(lèi)以及對(duì)應(yīng)的行,還會(huì)將具體的錯(cuò)誤類(lèi)別展示出來(lái)滔韵。有一個(gè)點(diǎn)要注意逻谦,在生成編譯文件的時(shí)候要加上一句
GCC_PRECOMPILE_PREFIX_HEADER=YES
,預(yù)先編譯prefix文件陪蜻,不然最終展示出來(lái)的報(bào)告會(huì)出現(xiàn)很多編譯問(wèn)題邦马,完整編譯語(yǔ)句如下:
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)行檢測(cè)滋将。
#!/bin/bash
COLOR_ERR="\033[1;31m" #出錯(cuò)提示
COLOR_SUCC="\033[0;32m" #成功提示
COLOR_QS="\033[1;37m" #問(wèn)題顏色
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ù)先檢測(cè)所需的安裝包是否存在
if which xcodebuild 2>/dev/null; then
echo 'xcodebuild exist'
else
echo 'xcodebuild 未安裝,請(qǐng)安裝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 未安裝症昏,請(qǐng)安裝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ù)随闽。//表示全局替換 /表示匹配到的第一個(gè)結(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代碼分析開(kāi)始??????????'$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可以檢測(cè)出來(lái)許許多多代碼不規(guī)范的地方肝谭,這些不規(guī)范會(huì)影響代碼的可讀性問(wèn)題掘宪,不易于維護(hù)蛾扇,時(shí)間長(zhǎng)了會(huì)造成很多技術(shù)債,但是更深層次的問(wèn)題比如資源泄漏添诉,內(nèi)存泄漏這類(lèi)問(wèn)題OCLint是無(wú)法檢測(cè)出來(lái)的屁桑,需要借助其他工具,比如Facebook的Infer栏赴,這個(gè)放到下一篇再去分享蘑斧。