记录一次oclint之旅

以前也了解过oclint,看了一下大概的相关规则,里面的自定义规则用的很少.
这次有空,详细的记录一下对一个工程的OCLint之旅.
然后针对OC的代码添加若干自定义规则.
当前软硬件环境:
Xcode 9.2
macOS High Sierra Version 10.13.4

Xcode工程结构

image.png

Xcode工程目录
image.png

相关目录的解释
BDSSSDK: SDK代码路径
BuildScript: 放置一些脚本文件,目前有自动生成framework脚本和oclint脚本(这篇文章的重点)
framework:可以先忽略,跟此篇文章没有关系
oclint_result:oclint分析后的结果存放的路径
其他的是iOS工程引用pod使用到的.
原始工程编译
image.png

TDSSAppOCLint.sh文件

myworkspace=../TDSSApp.xcworkspace # 替换workspace的名字
myscheme=TDSSApp # 替换scheme的名字
DATE=$(date +%Y-%m-%d-%H-%M-%S)
FILE_NAME=../oclint_result/oclint_result_$DATE.html
xcodebuild -workspace $myworkspace -scheme $myscheme clean&&
xcodebuild -workspace $myworkspace -scheme $myscheme \
-configuration Debug \
| xcpretty -r json-compilation-database -o compile_commands.json&&
oclint-json-compilation-database -e Pods -- \
-report-type html -o $FILE_NAME \
-rc LONG_LINE=200 \
-max-priority-1=100000 \
-max-priority-2=100000 \
-max-priority-3=100000; \
rm compile_commands.json;
if [ -f ./$FILE_NAME ]; then echo '-----分析完毕-----'
else echo "-----分析失败-----"; fi

执行方式
方法一: cd到BuildScript, 然后执行命令:

bash TDSSAppOCLint.sh 

方法二:添加Aggregate 的Target(例如名字为oclint)


image.png

添加成功后,添加Run Script


image.png

RunScript里的内容:
script_file_name=TDSSAppOCLint.sh
script_dir=${SRCROOT%/*}/BuildScript  #  坑爹啊 */
    script_path=$script_dir/$script_file_name
    # 获取目录的相关权限
    chmod +x $script_dir
    # 获取文件的相关权限
    chmod 764 $script_path
    # 调用sh脚本文件
    bash $script_path
    echo "run xcode script end"

用xcode选择oclint target后直接编译,对于当前我的项目出现如下的结果:


image.png

只要出现分析完毕,就表示成功产生oclint报告了,其他的错误可以先不用管.
注意一点的是:分析完毕前出现 Build Succeeded, 不会马上出现分析完毕,估计要等一会儿,这个时候一定不要着急,等几分钟后就会出现分析完毕.

报告分析
打开最新生产的一个oclint报告

image.png

反正是各种oclint的Warning.

下面就是对各种Rule Name的进行分析
所有的规则列表:https://oclint-docs.readthedocs.io/en/stable/rules/index.html?highlight=unused%20method%20parameter
Priority 3
unused method parameter(UnusedMethodParameter)

image.png

顾名思义未使用的函数参数:
仔细定位了相关文件的相关代码,发现主要是代理,事件中未使用到传入参数,在iOS里这个太正常了,一般都会在代理中多传一些参数的.因此可以把这个警告给去掉,所以要修改.sh文件.
实际上需要区分的是,Xcode会自带对未使用变量的警告的(这个是跟参数还是有区别的).
Xcode自带的未使用变量警告

Xcode自带的参数未使用警告

去除这个警告:

-rc LONG_LINE=200 \

后面加上一行:

-disable-rule UnusedMethodParameter \

重新用方法二得到oclint结果:


image.png

看前面的图:
总共是969个,unused method parameter是266,重新运行后剩下969-266=703!
表明我们已经成功忽略了这种警告

short variable name(ShortVariableName)和long variable name(LongVariableName)

变量名过短

这个过短(95个)一般在一些缩写中用到,例如:
i:循环变量的下标
vc:viewController
等等.
变量名过长

过长(175个),oc是一种自解释的语言,一般都是比较长的,
按照上述的方法,对这2个规则添加规则:

-disable-rule LongVariableName \
-disable-rule ShortVariableName \

编译oclint:
预期的结果是:703-95-175=433个.


image.png

long line(LongLine)

-rc LONG_LINE=200 

原本默认的是100,但是感觉OC的语言特点,我又比较喜欢在调用函数时写成一行,而不是写成多行,所以改成了200,但是感觉还是不够,我决定给他设置500


image.png
-rc LONG_LINE=500 

再次编译得到的结果:


image.png

还有一个:


image.png

image.png

Xcode有自动折行功能,我感觉我还是直接disable 此 rule 吧
-disable-rule LongLine \
image.png

unnecessary default statement in covered switch statement(UnnecessaryDefaultStatement)
此rule是表明没有必要的default语法,当在switch中枚举了所有的value后,就没有必要使用default了(https://oclint-docs.readthedocs.io/en/stable/rules/convention.html#unnecessarydefaultstatement).
这里描述下一些个人有关枚举的处理办法.
在代码中尽量不出现纯数字来switch 或者 if (xx==1) 之类的写法,一般的写法都是把所有有可能的数字定义成枚举,例如如下:

typedef NS_ENUM(NSInteger, BDSSOrderStatus) {
    BDSSOrderStatus_initilization = 0,
    BDSSOrderStatus_auto = 5,
    BDSSOrderStatus_published = 10,
    BDSSOrderStatus_assign_duty = 13,
    BDSSOrderStatus_accepted = 15,
    BDSSOrderStatus_accepted_duty = 17,
    BDSSOrderStatus_start = 20,
    BDSSOrderStatus_try_end = 25,
    BDSSOrderStatus_warning = 27,
    BDSSOrderStatus_end = 30,
    BDSSOrderStatus_end_by_sys = 31,
    BDSSOrderStatus_scored = 35,
    BDSSOrderStatus_canceled = 40,
    BDSSOrderStatus_invalid = 43,
    BDSSOrderStatus_reserve_2 = 45,
    BDSSOrderStatus_reserve_3 = 50
};

NSString *NSStringFromBDSSOrderStatus(BDSSOrderStatus status);

而绝大部分这些数据都是从服务端传给客户端的,然后再Model层对字段进行转换类似于如下:

@property (nonatomic, readonly) BDSSOrderStatus orderStatus;
- (BDSSOrderStatus)orderStatus
{
    return (BDSSOrderStatus)self.status;
}

这个时候就有可能遇到当服务器传status=51的时候(没有default的时候),在switch就会出现问题了,而且当时是在Debug编译模式下是没有问题,但到了Release编译模式就出现崩溃现象.最后发现是少了default,所以以后所有的switch都要在洗后写default,因此我也决定把这个规则给去掉.


image.png
-disable-rule UnnecessaryDefaultStatement

image.png

too few branches in switch statement(TooFewBranchesInSwitchStatement)
是switch分支太少,建议使用if语句,默认的是switch分支小于3个的时候,会出现警告,但是我喜欢使用switch,这样将来扩展也方便一下,因此有2种更改方法:
第一种是disable
第二种是把改成1(此篇文章我使用的方法):
image.png

image.png

-rc MINIMUM_CASES_IN_SWITCH=1

341-56=285:


image.png

unnecessary else statement(UnnecessaryElseStatement)
https://oclint-docs.readthedocs.io/en/stable/rules/redundant.html#unnecessaryelsestatement

image.png

那开始修改代码,修改成满足相关的写法(还好此项目只有15处,慢慢的改吧~~~)
image.png

为什么变成了268了? 我好想顺便修改了2个:ivar assignment outside accessors or init
ivar assignment outside accessors or init(AssignIvarOutsideAccessors)
https://oclint-docs.readthedocs.io/en/stable/rules/convention.html#assignivaroutsideaccessors
image.png

175个................
我感觉是有点历史遗留问题,我记得当时在MRC时代或者相关入门iOS书籍中都介绍了对属性的使用,那个时候都是用类似如下的代码:

_age = 1;
_nameLabel = [[UILabel alloc] init];

但是随着Xcode的升级与ARC的使用,我早已经改成了如下的规则:
尽量不要定义变量,能用属性就用属性,所有的属性前面访问都加self,尽量不要使用下划线开头的变量去设置属性,除非特殊情况.
所以导致还有这么多的warning警告.
又是一个大的工程!!!!!
改吧.
第一种

@property (nonatomic, strong) MSWeakTimer *timer;

- (void)endTimer {
    self.haveNewMessage = NO;
    if (_timer != nil) {
        [_timer invalidate];
        _timer = nil;
    }
}

修改为:

@property (nonatomic, strong) MSWeakTimer *timer;
- (void)endTimer {
    self.haveNewMessage = NO;
    if (self.timer != nil) {
        [self.timer invalidate];
        self.timer = nil;
    }
}

第二种

@interface BDSSGalleryViewController ()
{
    BOOL _onceToken;
}
@end

- (void)viewDidLayoutSubviews
{
    if (!_onceToken)
    {
        [self loadImage];
        _onceToken = YES;
    }
}

修改为:

@interface BDSSGalleryViewController ()
@property (nonatomic, assign) BOOL onceToken;
@end
- (void)viewDidLayoutSubviews
{
    if (!self.onceToken) {
        [self loadImage];
        self.onceToken = YES;
    }
}

第三种

// .h文件中
@property (nonatomic, assign, readonly) CGFloat isVisiable;
@property (nonatomic, assign, readonly) CGFloat keyboardHeight;
// .m文件中:
// 这里没有@synthesize isVisiable = _isVisiable;
@synthesize keyboardHeight = _keyboardHeight;

- (void)keyboardWillChangeFrame:(NSNotification *)notification
{
    // other code
    _isVisiable = endFrame.origin.y != [UIApplication sharedApplication].keyWindow.frame.size.height;
    _keyboardHeight = _isVisiable? endFrame.size.height: 0;
   // other code
}

修改为:

// .h文件中
@property (nonatomic, assign, readonly) CGFloat isVisiable;
@property (nonatomic, assign, readonly) CGFloat keyboardHeight;
// .m文件中:
@interface BDSSKitKeyboardInfo()
@property (nonatomic, assign) CGFloat isVisiable;
@property (nonatomic, assign) CGFloat keyboardHeight;
@end
- (void)keyboardWillChangeFrame:(NSNotification *)notification
{
    // other code
    self.isVisiable = endFrame.origin.y != [UIApplication sharedApplication].keyWindow.frame.size.height;
    self.keyboardHeight = _isVisiable? endFrame.size.height: 0;
   // other code
}

第四种

// .m文件中
@implementation BDSSKitDataRequest
{
    NSMutableArray *_requstUserIdArray; //待请求池
    BOOL _isRequesting;
}

- (void)afterReuquest:(NSArray *)userIds
{
    _isRequesting = NO;
    [_requstUserIdArray removeObjectsInArray:userIds];
    [self request];
}

注意跟第二种的区别,这里是放在@implementation中了
修改为:

@interface BDSSKitDataRequest()
@property (nonatomic, strong) NSMutableArray *requstUserIdArray; // 待请求池
@property (nonatomic, assign) BOOL isRequesting;
@end
- (void)afterReuquest:(NSArray *)userIds
{
    self.isRequesting = NO;
    [self.requstUserIdArray removeObjectsInArray:userIds];
    [self request];
}

初步改了一部分,现在的结果是:

image.png

parameter reassignment(ParameterReassignment)
https://oclint-docs.readthedocs.io/en/stable/rules/convention.html#parameterreassignment
修改传入的参数不是一个好习惯,所以需要消除这个warning,还好只有17个,消除的不会太累.......
image.png

修改后:
image.png

missing hash method(MissingHashMethod)这是Priority 1Warning
https://oclint-docs.readthedocs.io/en/stable/rules/cocoa.html#missinghashmethod

image.png

赶紧修复
image.png

useless parentheses(UselessParentheses)
https://oclint-docs.readthedocs.io/en/stable/rules/redundant.html#uselessparentheses
多余的括号的意思
样例一:(会产生warning)

CGFloat selfProtraitOriginX   = (cellWidth - self.cellPaddingToAvatar.x - protraitImageWidth);

样例二:(会产生warning)

frame.origin.x = MAX(frame.origin.x, CGRectGetMinX(contentFrame));

样例三:(不会产生warning)

frame.size.height = MIN(frame.size.height, maxSize.height);

样例一可以修改,但是样例二怎么修改??

define MIN(A,B) __NSMIN_IMPL__(A,B,__COUNTER__)

所以直接忽略吧.


image.png
-disable-rule UselessParentheses
image.png

最新的,当前的sh文件:

myworkspace=../TDSSApp.xcworkspace # 替换workspace的名字
myscheme=TDSSApp # 替换scheme的名字

DATE=$(date +%Y-%m-%d-%H-%M-%S)
FILE_NAME=../oclint_result/oclint_result_$DATE.html


xcodebuild -workspace $myworkspace -scheme $myscheme clean&&
xcodebuild -workspace $myworkspace -scheme $myscheme \
-configuration Debug \
| xcpretty -r json-compilation-database -o compile_commands.json&&
oclint-json-compilation-database -e Pods -- \
-report-type html -o $FILE_NAME \
-disable-rule UnusedMethodParameter \
-disable-rule LongVariableName \
-disable-rule ShortVariableName \
-disable-rule UnnecessaryDefaultStatement \
-disable-rule UselessParentheses \
-rc MINIMUM_CASES_IN_SWITCH=1 \
-disable-rule LongLine \
-max-priority-1=100000 \
-max-priority-2=100000 \
-max-priority-3=100000; \
rm compile_commands.json;
if [ -f ./$FILE_NAME ]; then echo '-----分析完毕-----'
else echo "-----分析失败-----"; fi
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容

  • # Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列...
    小迈克阅读 2,984评论 1 3
  • OCLint 安装方法 主要步骤 安装GodEyes_iOS 参考 GodEyes官网 http://godeye...
    liu_bo阅读 3,529评论 3 0
  • Code Review 代码评审,代码静态检查,Objective-C代码静态检查工具——OCLint Githu...
    BobWongs阅读 3,284评论 0 4
  • 初识OCLint OCLint是一个静态代码分析工具,提高质量和减少缺陷通过检查C 、C++ 和Objective...
    這Er阅读 14,367评论 97 61
  • 年轻,是我们的资本,是我们最大的资本,也许,你现在一无所有,也许你落魄异常,记住别放弃自己的梦想,因为你还有...
    海的枯萎8阅读 115评论 0 0