记录一次oclint之旅

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

Xcode工程结构

image.png

Xcode工程目录
记录一次oclint之旅_第1张图片
image.png

相关目录的解释
BDSSSDK: SDK代码路径
BuildScript: 放置一些脚本文件,目前有自动生成framework脚本和oclint脚本(这篇文章的重点)
framework:可以先忽略,跟此篇文章没有关系
oclint_result:oclint分析后的结果存放的路径
其他的是iOS工程引用pod使用到的.
原始工程编译
记录一次oclint之旅_第2张图片
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)


记录一次oclint之旅_第3张图片
image.png

添加成功后,添加Run Script


记录一次oclint之旅_第4张图片
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后直接编译,对于当前我的项目出现如下的结果:


记录一次oclint之旅_第5张图片
image.png

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

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

记录一次oclint之旅_第6张图片
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会自带对未使用变量的警告的(这个是跟参数还是有区别的).
记录一次oclint之旅_第7张图片
Xcode自带的未使用变量警告

记录一次oclint之旅_第8张图片
Xcode自带的参数未使用警告

去除这个警告:

-rc LONG_LINE=200 \

后面加上一行:

-disable-rule UnusedMethodParameter \

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


记录一次oclint之旅_第9张图片
image.png

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

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

记录一次oclint之旅_第10张图片
变量名过短

这个过短(95个)一般在一些缩写中用到,例如:
i:循环变量的下标
vc:viewController
等等.
记录一次oclint之旅_第11张图片
变量名过长

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

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

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


记录一次oclint之旅_第12张图片
image.png

long line(LongLine)

-rc LONG_LINE=200 

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


记录一次oclint之旅_第13张图片
image.png
-rc LONG_LINE=500 

再次编译得到的结果:


image.png

还有一个:


记录一次oclint之旅_第14张图片
image.png

记录一次oclint之旅_第15张图片
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:


记录一次oclint之旅_第16张图片
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];
}

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

记录一次oclint之旅_第17张图片
image.png

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

修改后:
记录一次oclint之旅_第18张图片
image.png

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

image.png

赶紧修复
记录一次oclint之旅_第19张图片
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
记录一次oclint之旅_第20张图片
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

你可能感兴趣的:(记录一次oclint之旅)