美文网首页
iOS LLVM-Clang 浅谈

iOS LLVM-Clang 浅谈

作者: 钟环 | 来源:发表于2020-08-23 09:15 被阅读0次
    LLVM概念
    1. LLVM官网: https://llvm.org/
    2. 编译器架构图:


      image
    • Frontend:前端 → 词法分析、语法分析、语义分析、生成中间代码(LLVM IR)
    • Optimizer:优化器 → 中间代码优化
    • Backend:后端 → 生成机器码
    • 传统编译器(如 CGG )的前端和后端没有完全分离,耦合在了一起,因而如果要支持一门新的语言或硬件平台,需要做大量的工作。


      image
    • 不同的前端后端使用统一的中间代码LLVM Intermediate Representation (LLVM IR)
        1. LLVM IR格式以 .ll结尾、以 .bc 的二进制格式结尾、内存格式
        1. Bitcode(Xcode 7之后)就是以.bc结尾的中间代码,是LLVM-IR在磁盘上的一种二进制表示形式。例如:clang -c -emit-llvm xxxx.m 生成 xxxx. bc
        1. 如果要转换成文本格式查看,例如:llvm-dis xxxx.bc -o xxxx.ll
        1. 苹果单独对 Bitcode 进行了额外的优化.
          • i) 应用上传到 AppStore时,Xcode会将程序对应的 Bitcode一起上传;
          • ii) AppStore会将 Bitcode重新编译为可执行程序,供用户下载;
          • iii) Bitcode被Xcode打包成 xar文档,嵌入的 MachO中。
    • 如果需要支持一种新的编程语言、硬件设备,那么只需要实现一个新的前后端
    1. LLVM 同时支持 AOT 预先编译和 JIT 即时编译
    Clang概念
    1. 官网:http://clang.llvm.org/
    2. Clang项目为LLVM 项目的C语言系列(C,C ++,Objective C / C ++,OpenCL,CUDA和RenderScript)中的语言提供语言前端和工具基础结构。提供了与GCC兼容的编译器驱动程序(clang)和与MSVC兼容的编译器驱动程序(clang-cl.exe)
    3. 说白了就是LLVM项目的一个子项目,LLVM的C语言家族前端。
    4. 相比于GCC,Clang具有如下优点
      • 编译速度快:在某些平台上,Clang的编译速度显著的快过GCC(Debug模式下编译OC速度比GGC快3倍)
      • 占用内存小:Clang生成的AST所占用的内存是GCC的五分之一左右
      • 模块化设计:Clang采用基于库的模块化设计,易于IDE 集成及其他用途的重用
      • 诊断信息可读性强:在编译过程中,Clang 创建并保留了大量详细的元数据(metadata),有利于调试和错误报告
      • 设计清晰简单,容易理解,易于扩展增强
    编译流程
    image
    1. Objective-C与swift都采用Clang作为编译器前端,编译器前端主要进行词法分析、语法分析、语义分析、生成中间代码,在这个过程中,会进行类型 检查,如果发现错误或者警告会标注出来在哪一行。
    2. 编译器后端会进行机器无关的代码优化,生成机器语言,并且进行机器相关的代码优化,根据不同的系统架构生成不同的机器码。


      image
    自定义规则
    image

    执行oclint自带脚本scaffoldRule:
    cd ~/oclint/oclint-scripts
    ./scaffoldRule Property -t ASTVisitor(参数可选为Generic,SourceCodeReader,ASTVisitor,ASTMatcher)

    加载自定义规则Xcode项目

    oclint-xcoderules目录下,执行create-xcode-rules.sh脚本

    #! /bin/sh -e
    cmake -G Xcode \
            -D CMAKE_CXX_COMPILER=../build/llvm-install/bin/clang++ \ 
            -D CMAKE_C_COMPILER=../build/llvm-install/bin/clang \
            -D OCLINT_BUILD_DIR=../build/oclint-core \
            -D OCLINT_SOURCE_DIR=../oclint-core \
            -D OCLINT_METRICS_SOURCE_DIR=../oclint-metrics 
            \ -D OCLINT_METRICS_BUILD_DIR=../build/oclint-metrics \
            -D LLVM_ROOT=../build/llvm-install/../oclint-rules
    
    编译自定义项目生成dylib文件

    复制到~/oclint/build/oclint-release/lib/oclint/rules下

    调试规则
    WX20200822-165241.png

    以下编译环境是在测试demo中的,添加到scheme下的run配置中:


    WX20200822-165317.png
    -R=/Users/libing/oclint/oclint-xcoderules/rules.dl/Debug /Users/libing/oclint/oclint-xcodebuild/TestRule/TestRule/ViewController.m -- -x objective-c -isystem ~/oclint/build/oclint-release/lib/clang/5.0.1/include/ -arch x86_64 -fmessage-length=0 -fdiagnostics-show-note-include- stack -fmacro-backtrace-limit=0 -std=gnu11 -fobjc-arc -fobjc-weak -fmodules -gmodules -fmodules-cache-path=/Users/libing/Library/Developer/Xcode /DerivedData/ModuleCache.noindex -fmodules-prune-interval=86400 -fmodules-prune-after=345600 -fbuild-session-file=/Users/libing/Library/Developer/Xcode /DerivedData/ModuleCache.noindex/Session.modulevalidation -fmodules-validate-once-per-build-session -Wnon-modular-include-in-framework-module - Werror=non-modular-include-in-framework-module -Wno-trigraphs -fpascal-strings -O0 -fno-common -Wno-missing-field-initializers -Wno-missing-prototypes - Werror=return-type -Wdocumentation -Wunreachable-code -Wno-implicit-atomic-properties -Werror=deprecated-objc-isa-usage -Wno-objc-interface-ivars - Werror=objc-root-class -Wno-arc-repeated-use-of-weak -Wimplicit-retain-self -Wduplicate-method-match -Wno-missing-braces -Wparentheses -Wswitch - Wunused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wconditional-uninitialized - Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum- conversion -Wno-float-conversion -Wnon-literal-null-conversion -Wobjc-literal-conversion -Wshorten-64-to-32 -Wpointer-sign -Wno-newline-eof -Wno- selector -Wno-strict-selector-match -Wundeclared-selector -Wdeprecated-implementations -DDEBUG=1 -DOBJC_OLD_DISPATCH_PROTOTYPES=0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.2.sdk -fasm-blocks -fstrict-aliasing - Wprotocol -Wdeprecated-declarations -mios-simulator-version-min=12.2 -g -Wno-sign-conversion -Winfinite-recursion -Wcomma -Wblock-capture- autoreleasing -Wstrict-prototypes -Wno-semicolon-before-method-body -Wunguarded-availability -fobjc-abi-version=2 -fobjc-legacy-dispatch -index-store- path /Users/libing/Library/Developer/Xcode/DerivedData/TestRule-fnyamuwmajnavbcsgeuhbzloxpbs/Index/DataStore -iquote /Users/libing/Library/Developer /Xcode/DerivedData/TestRule-fnyamuwmajnavbcsgeuhbzloxpbs/Build/Intermediates.noindex/TestRule.build/Debug-iphonesimulator/TestRule.build/TestRule- generated-files.hmap -I/Users/libing/Library/Developer/Xcode/DerivedData/TestRule-fnyamuwmajnavbcsgeuhbzloxpbs/Build/Intermediates.noindex/TestRule.build /Debug-iphonesimulator/TestRule.build/TestRule-own-target-headers.hmap -I/Users/libing/Library/Developer/Xcode/DerivedData/TestRule- fnyamuwmajnavbcsgeuhbzloxpbs/Build/Intermediates.noindex/TestRule.build/Debug-iphonesimulator/TestRule.build/TestRule-all-target-headers.hmap -iquote /Users/libing/Library/Developer/Xcode/DerivedData/TestRule-fnyamuwmajnavbcsgeuhbzloxpbs/Build/Intermediates.noindex/TestRule.build/Debug-iphonesimulator /TestRule.build/TestRule-project-headers.hmap -I/Users/libing/Library/Developer/Xcode/DerivedData/TestRule-fnyamuwmajnavbcsgeuhbzloxpbs/Build/Products /Debug-iphonesimulator/include -I/Users/libing/Library/Developer/Xcode/DerivedData/TestRule-fnyamuwmajnavbcsgeuhbzloxpbs/Build/Intermediates.noindex /TestRule.build/Debug-iphonesimulator/TestRule.build/DerivedSources-normal/x86_64 -I/Users/libing/Library/Developer/Xcode/DerivedData/TestRule- fnyamuwmajnavbcsgeuhbzloxpbs/Build/Intermediates.noindex/TestRule.build/Debug-iphonesimulator/TestRule.build/DerivedSources/x86_64 -I/Users/libing/Library /Developer/Xcode/DerivedData/TestRule-fnyamuwmajnavbcsgeuhbzloxpbs/Build/Intermediates.noindex/TestRule.build/Debug-iphonesimulator/TestRule.build /DerivedSources -F/Users/libing/Library/Developer/Xcode/DerivedData/TestRule-fnyamuwmajnavbcsgeuhbzloxpbs/Build/Products/Debug-iphonesimulator -MMD - MT dependencies -MF /Users/libing/Library/Developer/Xcode/DerivedData/TestRule-fnyamuwmajnavbcsgeuhbzloxpbs/Build/Intermediates.noindex/TestRule.build /Debug-iphonesimulator/TestRule.build/Objects-normal/x86_64/ViewController.d --serialize-diagnostics /Users/libing/Library/Developer/Xcode/DerivedData /TestRule-fnyamuwmajnavbcsgeuhbzloxpbs/Build/Intermediates.noindex/TestRule.build/Debug-iphonesimulator/TestRule.build/Objects-normal/x86_64 /ViewController.dia -c /Users/libing/oclint/oclint-xcodebuild/TestRule/TestRule/ViewController.m -o /Users/libing/Library/Developer/Xcode/DerivedData/TestRule- fnyamuwmajnavbcsgeuhbzloxpbs/Build/Intermediates.noindex/TestRule.build/Debug-iphonesimulator/TestRule.build/Objects-normal/x86_64/ViewController.o
    

    备注:-R=/Users/libing/oclint/oclint-xcoderules/rules.dl/Debug /Users/libing/oclint/oclint-xcodebuild/TestRule/TestRule/ViewController.m -- -x objective-c - isystem ~/oclint/build/oclint-release/lib/clang/5.0.1/include/ 加上图上从-arch x86_64 开始复制到最后,拼起来放到自定义规则的scheme的run配置中。

    image
    查看编译过程

    clang -ccc-print-phases ViewController.m

    0: input, "ViewController.m", objective-c
    1: preprocessor, {0}, objective-c-cpp-output → 预编译
    2: compiler, {1}, ir → 前端编译,生成中间代码IR
    3: backend, {2}, assembler → 后端Backend 4: assembler, {3}, object → 生成目标.o文件 5: linker, {4}, image → 链接
    6: bind-arch, "x86_64", {5}, image → 绑定架构
    
    查看 preprocessor(预处理)

    clang -E ViewController.m

    image

    注意:出现fatal error: 'UIKit/UIKit.h' file not found,但不影响查看结果,如果不报错指定一下sdk
    clang -E -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk ViewController.m

    词法分析,生成Token

    clang -fmodules -E -Xclang -dump-tokens ViewController.m

    image
    语法分析,生成抽象AST语法树
    image

    通过语法分析可以清晰的看到语法树中的节点,对应的节点Decl(声明) / Stmt(语句、表达式),通过Google 搜索:clang XXX 可以直接定位到clang的该节 点文档说明。

    // 分析所有声明
    bool VisitDecl(Decl *decl) {
        return true;// 返回true以继续遍历AST,返回false以终止遍历,退出Clang 
    }
    // 分析表达式
    bool VisitStmt(Stmt *S) {
        return true;// 返回true以继续遍历AST,返回false以终止遍历,退出Clang 
    }
    
    自定义规则源码

    OClintScripts

    自定义属性规范
    1. nonatomic 与 atomic 修饰:遗忘或错用了 atomic 修饰
    bool VisitObjCPropertyDecl(ObjCPropertyDecl *node) { 
      if (node->isAtomic()) {
        string description = "不应该用atomic修饰 " + node->getNameAsString() + " 请改用nonatomic";
        addViolation(node, this, description);//发出警告
      } 
    }
    

    代码解析: 通过AST抽象语法树分析:


    image

    入口在VisitObjCPropertyDecl方法,那么在此方法中断点验证确实如此,接下来分析,属性中是否是atomic修饰,凭着灵感发现确实自动补全了isAtomic 方法,也可以这么写:

    ObjCPropertyDecl::PropertyAttributeKind attributeKind = node->getPropertyAttributes(); 
        if(!(attributeKind & ObjCPropertyDecl::OBJC_PR_nonatomic)){
        string description = "不应该用atomic修饰 " + node->getNameAsString() + " 请改用nonatomic";
        addViolation(node, this, description); //发出警告
    }
    
    1. NSString 应尽量用 copy 修饰,避免用 strong 修饰
    if(starts_with(typeStr, "NSString") && !(attributeKind & ObjCPropertyDecl::OBJC_PR_copy)) {
        addViolation(node, this, typeStr + node->getNameAsString() + " 应尽量用 copy 修饰"); 
    }
    
    1. Delegate 应该用 weak 修饰
    if(typeStr.find("<")!=string::npos && typeStr.find(">")!=string::npos && starts_with(node->getType().getAsString(), "id")){ if(!(attributeKind & ObjCPropertyDecl::OBJC_PR_weak)){
        cout<< node->getType().getAsString()<<endl;
        addViolation(node, this, node->getNameAsString() + " 尽可能用 weak 修饰"); }
    }
    
    1. Block 应该用 copy / strong 修饰
    if (node->getType()->isBlockPointerType()) {
        if(!((attributeKind & ObjCPropertyDecl::OBJC_PR_copy) || (attributeKind & ObjCPropertyDecl::OBJC_PR_strong))){ 
        addViolation(node, this, node->getNameAsString() + " 应该用 copy / strong 修饰");
        } 
    }
    
    1. 属性名称应该遵循驼峰命名,不应该以大写或_开头
    string name = node->getNameAsString(); 
    string::iterator itor = name.begin(); 
    if(*itor >= 'A' && *itor <= 'Z'){
        addViolation(node, this, "Property "+name+" 应该遵循驼峰命名,不应该以大写字母开头"); }
    if(*itor == '_'){
        addViolation(node, this, "Property "+name+" 应该遵循驼峰命名,不应该以下划线开头");
    }
    
    1. 成员变量应该以_开头
    bool VisitObjCIvarDecl(ObjCIvarDecl *node) {
        string name = node->getNameAsString(); 
        string::iterator itor = name.begin(); 
        if(*itor != '_'){
            addViolation(node, this, "成员变量 "+name+" 应该以下划线开头"); 
        }
        return true;
    }
    
    自定义空格规范
    1. 方法前面-或+请遵循空格规范、方法大括号请遵循空格规范
        bool VisitObjCMethodDecl(ObjCMethodDecl *node)
        {
            string methodDeclStr;
            ASTContext *context = _carrier->getASTContext();
            SourceLocation begin = node->getSourceRange().getBegin();
            SourceLocation end = node->getSourceRange().getEnd(); methodDeclStr.assign(context->getSourceManager().getCharacterData(begin),end.getRawEncoding()-begin.getRawEncoding());
            for(string::iterator it = methodDeclStr.begin(); it!= methodDeclStr.end(); it++)
            {
                if (*it =='-' || *it =='+') {
                    if (it+2 < methodDeclStr.end()) {
                        if (!((*(it+1) == ' ')&&(*(it+2) == '('))) {
                            addViolation(node, this, "方法前面-或+请遵循空格规范");
                            return true;
                        }
                    }
                }
                
                if (*it =='{') {
                    if (it-1 >= methodDeclStr.begin()) {
                        if (*(it-1) != ' ' && *(it-1) != '\n') {
                            addViolation(node, this, "方法大括号请遵循空格规范");
                        }
                    }
                    return true;
                }
            }
            return true;
        }
    };
    
    1. @property左括号、右括号、中间逗号、*修饰需要遵循空格规范
    bool VisitObjCPropertyDecl(ObjCPropertyDecl *node)
        {
            string methodDeclStr;
            ASTContext *context = _carrier->getASTContext();
            SourceLocation begin = node->getSourceRange().getBegin();
            SourceLocation end = node->getSourceRange().getEnd(); methodDeclStr.assign(context->getSourceManager().getCharacterData(begin),end.getRawEncoding()-begin.getRawEncoding());
    
            for(string::iterator it = methodDeclStr.begin(); it!= methodDeclStr.end(); it++)
            {
                if (*it == '(') {
                    if (it-2 >= methodDeclStr.begin()) {
                        if (!(*(it-1) == ' ' && *(it-2) == 'y') && *(it+1) != '^') {
                            addViolation(node, this, node->getNameAsString() + "@property左括号请遵循空格规范");
                            return true;
                        }
                    }
                }
                
                if (*it == ',') {
                    if (it+1 < methodDeclStr.end()) {
                        if (*(it+1) != ' ') {
                            addViolation(node, this, node->getNameAsString() + "@property中逗号请遵循空格规范");
                            return true;
                        }
                    }
                }
                
                if (*it == ')') {
                    if (it+1 < methodDeclStr.end()) {
                        if (*(it+1) != ' ') {
                            addViolation(node, this, node->getNameAsString() + "@property右括号请遵循空格规范");
                            return true;
                        }
                    }
                }
                
                if (*it == '*') {
                    if (it-1 >= methodDeclStr.begin()) {
                        if (*(it-1) != ' ') {
                            addViolation(node, this, node->getNameAsString() + "@property属性*修饰请遵循空格规范");
                            return true;
                        }
                    }
                }
            }
            return true;
        }
    
    自定义枚举规范
    1. NS_ENUM/NS_OPTIONSenum
      经过调试发现enum枚举走VisitEnumDecl方法,而NS_ENUM/NS_OPTIONS均不走。


      image
    bool VisitEnumDecl(EnumDecl *node)
    {
            addViolation(node, this, node->getNameAsString()+"枚举尽量使用NS_ENUM/NS_OPTIONS");
            return true;
    }
    

    或者基于AbstractASTMatcherRule匹配

    virtual void callback(const MatchFinder::MatchResult &result) override {
        const EnumDecl *enumDecl = result.Nodes.getNodeAs<EnumDecl>("enumDecl"); 
        if (enumDecl)
        {
            addViolation(node, this, "枚举尽量使用NS_ENUM/NS_OPTIONS"); 
        }
    }
    virtual void setUpMatcher() override {
        addMatcher(enumDecl().bind("enumDecl")); 
    }
    
    1. 枚举不要下划线开头
    bool VisitEnumConstantDecl(EnumConstantDecl *node)
    {
        string name = node->getNameAsString();
        string::iterator itor = name.begin();
        if(*itor == '_'){
           addViolation(node, this, "枚举不要下划线开头");
        }
        return true;
    }
    
    自定义类名规范

    类名使用大写字母开头

    bool VisitObjCProtocolDecl(ObjCProtocolDecl *node)
    {
            string name = node->getNameAsString();
            string::iterator itor = name.begin();
            if(!(*itor >= 'A' && *itor <= 'Z')){
                addViolation(node, this, name+"协议名请用大写字母开头");
            }
            return true;
    }
    
      
    bool VisitObjCImplDecl(ObjCImplDecl *node)
    {
            string name = node->getNameAsString();
            string::iterator itor = name.begin();
            if(!(*itor >= 'A' && *itor <= 'Z')){
                addViolation(node, this, name+"类名请用大写字母开头");
            }
            return true;
    }
    
    自定义方法格式规范

    方法参数超过3个,需要折行并冒号对齐

    bool VisitObjCMethodDecl(ObjCMethodDecl *node)
    {
        string name = node->getNameAsString();
        if( node->param_size()>_threshold){
            string methodDeclStr;
            ASTContext *context = _carrier->getASTContext();
            SourceLocation begin = node->getSourceRange().getBegin();
            SourceLocation end = node->getSourceRange().getEnd(); methodDeclStr.assign(context->getSourceManager().getCharacterData(begin),end.getRawEncoding()-node->getSourceRange().getBegin().getRawEncoding());
            string replaceName = formatObjcMethodName(methodDeclStr);
            if(methodDeclStr.find(replaceName)){
                addViolation(node, this, "方法参数超过"+to_string(_threshold)+"个,需要折行并冒号对齐");
            }
        }
        return true;
    }
    
    修正oclint误报规范

    PreferEarlyExit:使用提前退出/继续来简化代码并减少缩进
    误报:懒加载与init初始化误判
    修正思路:识别懒加载与init方法进行判断

    bool starts_with(const string& s1, const string& s2) {
        return s2.size() <= s1.size() && s1.compare(0, s2.size(), s2) == 0;
    }
    // 将Property入栈到数组与懒加载比较是否selector相同
    bool VisitObjCPropertyDecl(ObjCPropertyDecl *node) {
        if(std::find(_arraySelector.begin(), _arraySelector.end(), node->getNameAsString()) == _arraySelector.end()) {
            _arraySelector.push_back(node->getNameAsString());
        }
        return true;
    }
    //以init开头的都认为是初始化方法
    bool VisitObjCMethodDecl(ObjCMethodDecl *node) {
        string selectorName = node->getSelector().getAsString();
        if (starts_with(selectorName, "init") || std::find(_arraySelector.begin(), _arraySelector.end(), selectorName) != _arraySelector.end() ) {
            _isSysSelector = true; 
        } else {
            _isSysSelector = false; 
        }
        return true; 
    }
    //通过_isSysSelector修正误报
    bool VisitCompoundStmt(CompoundStmt* compoundStmt) {
        if (compoundStmt->size() < 2) {
            return true; 
        }
        if (_isSysSelector) { 
            return true;
        }
        auto last = compoundStmt->body_rbegin();
        if (isFlowOfControlInterrupt(*last)) {
            addViolationIfStmtIsLongIf(*++last);
        }
        return true; 
    }
    
    自定义内存泄漏检测

    由于目前xcode对block循环引用在编译时期已有提示:capturing 'self' strongly in this block is likely to lead to a retain cycle,但如果API 中的block并未检测。新增了此处关于block强引用的检测。

    virtual void setUp() override {
        _teamname = RuleConfiguration::stringForKey("TEAM_NAME", "xxxxx");       
        this->ignoreArr.push_back("UIView"); // 忽略UIView类的block检测 
    }
    virtual void tearDown() override {}
    bool isMasnoryBlock(BlockDecl *node) {
        for (BlockDecl::param_iterator iterator = node->param_begin() ; iterator != node->param_end(); iterator ++) 
        {
           if ((*iterator)->getType().getAsString().find("MASConstraintMaker") != string::npos) return true;
           return false;
        }
    bool isIgnoreArrClass(ObjCMessageExpr *node) {
        string type = node->getClassReceiver().getAsString();
        vector<string>::iterator ret = std::find(this->ignoreArr.begin(), this->ignoreArr.end(), type); 
        if(ret == this->ignoreArr.end()) {
            return false; 
        } else {
            return true;
        }
    }
    bool VisitObjCMessageExpr(ObjCMessageExpr *node) 
    {
        if (this->isIgnoreArrClass(node)) {
            return true; 
        }
        int argCount = node->getNumArgs(); 
        Expr **exprArray = node->getArgs(); 
        for (int i = 0; i < argCount; i ++ ) {
            BlockExpr *expr = dyn_cast_or_null<BlockExpr>(exprArray[i]); 
            if (expr && expr->getBlockDecl()) {
                BlockDecl *blockDecl = expr->getBlockDecl();
                for (BlockDecl::capture_const_iterator iterator = blockDecl->capture_begin() ; iterator != blockDecl->capture_end(); iterator ++) {
                    ImplicitParamDecl *implicitParamDecl = dyn_cast_or_null<ImplicitParamDecl>(iterator->getVariable());
                    if (implicitParamDecl && implicitParamDecl->getName() == "self") {
                        if (!isMasnoryBlock(blockDecl)) { 
                            string methodDeclStr;
                            ASTContext *context = _carrier->getASTContext();
                            SourceLocation begin = this->methodDecl->getSourceRange().getBegin();
                            SourceLocation end = this->methodDecl->getSourceRange().getEnd(); 
                            methodDeclStr.assign(context->getSourceManager().getCharacterData(begin),end.getRawEncoding()-begin.getRawEncoding());
                            string methodblock;
                            SourceLocation beginBlock = node->getSourceRange().getBegin();
                            SourceLocation endBlock = node->getSourceRange().getEnd(); 
                            methodblock.assign(context->getSourceManager(). getCharacterData(beginBlock),endBlock.getRawEncoding()-beginBlock.getRawEncoding());
                            if (methodDeclStr.find(methodblock)!=string::npos && methodDeclStr.find("@weakify(self)")==string::npos && methodDeclStr.find("__weak")==string::npos && node->getSelector().getAsString().find("animateWithDuration:")==string::npos) 
                            {
                                addViolation(blockDecl, this,"block中强引用了self,注意使用weak修饰self"); 
                            }
                        }
                    }
                }
            }
        }
        return true;
    }
    bool VisitObjCMethodDecl(ObjCMethodDecl *node) {
        this->methodDecl = node;
        return true; 
    }
    

    相关文章

      网友评论

          本文标题:iOS LLVM-Clang 浅谈

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