如何優雅地調試

幾乎全部高級語言的編譯過程都是預處理>編譯>彙編>連接,而gcc是應用最廣的編譯器了。可是apple嫌棄他太臃腫,因而就有了Clang(編譯器前端),LLVM(編譯器架構,本質是一個庫),LLDB(調試器)html

效率低下的調試方法

打斷點配合NSLog,這真是最樸素的調試方式。
NSLog效率低下的緣由及嘗試lldb斷點打印Log這篇博客裏有個測試,NSLog和printf的效率差了100多倍。調試的NSLog還好,記得本身刪掉就行,可是代碼裏充斥着許多的NSLog日誌,確定是消耗性能的。
通常是這樣處理:前端

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

不過調試的時候,遇到個問題,想到了NSLog,而後還要從新跑,如此循環,非常浪費時間。
還好有LLDB,他的經常使用調試命令必定要緊緊記住,而且用在調試中,會事半功倍。
express

LLDB經常使用命令

與調試器共舞 - LLDB 的華爾茲
這篇博客介紹的很詳細,本身對照着玩一下就能記住了,我大體把經常使用的歸類了下xcode

命令 做用 備註
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)
app

condition是設置觸發斷點的條件,action是觸發斷點後能夠執行的操做(能夠添加多個action)。ide

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以後就能夠執行自定義命令啦。

相關文章
相關標籤/搜索