美文网首页CI/CD
记录一次oclint之旅

记录一次oclint之旅

作者: 传说中的汽水枪 | 来源:发表于2018-06-19 21:13 被阅读6次

    以前也了解过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
    

    相关文章

      网友评论

        本文标题:记录一次oclint之旅

        本文链接:https://www.haomeiwen.com/subject/hauzeftx.html