zoukankan      html  css  js  c++  java
  • 如何优雅地调试

    几乎所有高级语言的编译过程都是预处理>编译>汇编>链接,而gcc是应用最广的编译器了。但是apple嫌弃他太臃肿,于是就有了Clang(编译器前端),LLVM(编译器架构,本质是一个库),LLDB(调试器)

    效率低下的调试方法

    打断点配合NSLog,这真是最朴素的调试方式。
    NSLog效率低下的原因及尝试lldb断点打印Log这篇博客里有个测试,NSLog和printf的效率差了100多倍。调试的NSLog还好,记得自己删掉就行,但是代码里充斥着许多的NSLog日志,肯定是消耗性能的。
    一般是这样处理:

    #ifdef DEBUG
    #define DebugLog(...) NSLog(@"%s 第%d行 
     %@
    
    ", __func__, __LINE__, [NSString stringWithFormat:__VA_ARGS__])
    #define DebugFunctionLog() NSLog(@"
    ==============================================================================
    className : %s
    classFunction : %s
    classFunctionLine : %d
    ==============================================================================", object_getClassName(self),__PRETTY_FUNCTION__,__LINE__)
    #else
    #define DebugLog(...) /* */
    #define DebugFunctionLog(...) /* */
    #endif
      
    

    不过调试的时候,遇到个问题,想到了NSLog,然后还要重新跑,如此循环,很是浪费时间。
    还好有LLDB,他的常用调试命令一定要牢牢记住,并且用在调试中,会事半功倍。

    LLDB常用命令

    与调试器共舞 - LLDB 的华尔兹
    这篇博客介绍的很详细,自己对照着玩一下就能记住了,我大致把常用的归类了下

    命令 作用 备注
    help 列举出所有命令 help <command>了解更多细节
    print(p) 打印值 会有美元符开头的字符,类似$0,可以对他操作
    expression(e) 修改值 --表示参数结束,e --没有任何参数,相当于print
    e -O --(po) 打印对象详情 /x十六进制,/t二进制...(p也适用)
    c,n,s,finish 流程控制 对应xcode框上的四个按钮
    frame info 当前的行数和源码文件等信息
    thread return ... 改变函数返回值 这个超级有用,不用在模拟数据测试了
    • p,po打印变量可以以各种格式打印出,完整清单
    • LLDB无法确定涉及的类型,我以前还以为不能打印出来,其实是需要强制转换告诉LLDB类型
        LYModel *person = [[LYModel alloc] init];
        person.name = @"luyang";
        person.age = 10;
        NSArray *personArray = @[person];
    
    (lldb) p personArray[0].name
    error: property 'name' not found on object of type 'id'
    (lldb) p ((LYModel *)person).name
    (__NSCFConstantString *) $0 = 0x0000000107a17100 @"luyang"  
    
    

    断点


    一共有6种类型的断点,最常见的有3种:

    • 行为断点,就是点在行数上的蓝色点,右滑会做个小动画消失的断点。可以设置condition和action。
    • Symbolic Breakpoint,可以设定一些方法名(symbol),执行到的时候停止程序。
    • Exception Breakpoint,主要是出现异常的时候停止程序,开在那边好处多多,当程序崩的时候,很多情况下可以直接定位到错误。

    以前一直以为断点只是让程序断住,直到发现了Edit Breakpoint。主要有2个功能,condition,action。(Exception Breakpoint是出异常一定会停止,所以没有condition)

    condition是设置触发断点的条件,action是触发断点后可以执行的操作(可以添加多个action)。

        for (int i = 0; i < 10; i++) {
            
        }  
    
    


    • Ignore是指condition判断忽略的次数,比如设置为2,那么i = 0,i = 1不会进入condition判断,所以也肯定不会停止程序。
    • Automatically continue after evaluating actions就如他的意思,执行完action后不会停止程序。

    Breakpoint面板上的操作可以全部由LLDB调试器的命令来实现,不过还是觉得这个在面板里操作更方便些。

    Chisel

    LLDB调试器还能玩些更疯狂的事,更新UI,pushVC等等。不过用原生的命令会比较难记,正好facebook开源的LLDB插件Chisel完美地解决了这个问题,可以让你的调试更Easy。

    安装Chisel

    brew update
    brew install chisel
    
    

    安装完成按照安装日志上的提示,在~/.lldbinit文件中添加一行,没有则新建

    常用命令

    • pviews & pvc 层级打印views,viewcontroller

    • fc & fvc 通过正则搜索view,viewcontroller,可以正则搜索

    • visualize 预览打开UIImage,UIView,CALayer,可以先搜索,在用地址打开

    (lldb) visualize 0x7fcf6fd17f90  
    
    

    • show & hide 显示和隐藏指定的View,效果立刻出现
    (lldb) hide 0x7fcf6fd17f90  
    
    

    • border/unborder 给指定view加上边框
    (lldb) border 0x7fcf6fd17f90  
    
    

    • caflush 当坐标或者颜色改变了,不需要继续,直接caflush就可以刷新界面
    (lldb) e (void)[0x7fcf6fd17f90 setBackgroundColor:[UIColor greenColor]]
    (lldb) caflush  
    
    

    • bmessage 打断点用的,比原生和面板好的地方是,即使子类没有实现的方法,一样会在父类里打,并且做判断[self isKindOfClass:[MyViewController class]],不会打到别的继承类上面去。这样就不用麻烦地重写一个空函数,例如viewWillAppear:
    (lldb) bmessage -[MyViewController viewWillAppear:]  
    
    

    自定义命令
    还可以执行自定义命令,非常简单。
    就是写个类,实现name,description, run3个方法。

    • name 返回值就是自定义命令
    • description 返回值就是描述,help 会返回描述
    • run 运行函数,执行的结果就会显示在window上
    # example.py
    import lldb
    import fblldbbase as fb
    
    def lldbcommands():
        return [ testCommand() ]
    
    class testCommand(fb.FBCommand):
        def name(self):
            return 'levi'
    
        def description(self):
            return 'this is levi command'
    
        def run(self, arguments, options):
            print arguments  
    
    


    这是很简单的例子,arguments会接收参数,从而实现很多的功能,还可以lldb.debugger.HandleCommand(...)执行lldb别的命令来返回值,随心所欲。

    把example.py的位置放进~/.lldbinit里

    重启xcode之后就可以执行自定义命令啦。

  • 相关阅读:
    java 验证码
    时间日期转换+两个日期相减
    java创建文件和目录
    java获取文件名的三种方法
    获取当前日期前100天的日期
    字符串去重
    Java,数据库中的数据导入到Excel
    两个list<object> 比较 得到相同数据 差异数据
    Springmvc中@RequestParam传值中文乱码解决方案
    将src非空的属性注入到des中
  • 原文地址:https://www.cnblogs.com/stevenfukua/p/6754505.html
Copyright © 2011-2022 走看看