iOS 開發(fā)者旅途中的指南針 - LLDB 調(diào)試技術

今天給大家介紹的內(nèi)容攀甚,無關乎任何功能性開發(fā)技術秋度,但又對開發(fā)的效率影響至深,這就是調(diào)試技術埠居。

何為調(diào)試呢事期,比如我們用 print 函數(shù)在指定位置進行輸出兽泣,來定位某些節(jié)點的變量內(nèi)的取值:

let result = parseJSON("[1,2,3]");
print(result);

result = parseJSON("error");
print(result);

相信我們大家看到類似這樣的代碼都不會陌生唠倦,估計為開發(fā)者朋友都會或多或少的用這樣的方式對程序進行調(diào)試。

這種方式有它的方便之處法希,就是我們不需要太多思考靶瘸,需要跟蹤某些地方的時候怨咪,直接輸出就可以得到調(diào)試信息了。但這樣做也有它的弊端唉匾,就是我們每次這樣調(diào)試巍膘,都要反復的編譯峡懈,運行与斤,然后寫進新的 print 語句撩穿,再繼續(xù)編譯食寡,運行。反復的編譯善榛,運行會比較消耗時間锭弊。并且我們再調(diào)試完之后擂错,很容易會忘記將調(diào)試語句刪除钮呀,導致很多輸出語句遺留再代碼中爽醋,隨著項目的長期進展后蚂四,這樣會對項目后期的調(diào)試造成很多干擾。

而且久妆,當我們想再次調(diào)試這段區(qū)域的時候筷弦,我們不得不再次寫上這些輸出語句烂琴。而有時對于稍微復雜一些的調(diào)試場景蜕乡,print 輸出這樣的方式异希,往往還不能太好的應對称簿。

那么有什么辦法能解決這些麻煩呢憨降,那就是調(diào)試技術,調(diào)試器幾乎在大多數(shù)現(xiàn)代的開發(fā)環(huán)境中都會有士嚎,所以莱衩,iOS 開發(fā)也不例外笨蚁,Xcode 環(huán)境為我們提供的對應調(diào)試工具就是 LLDB括细。

初識 LLDB

LLDB 就是 XCode 為我們提供的調(diào)試工具。那么說了這么多锉试,到底什么是調(diào)試工具呢呆盖? 也許說調(diào)試器你可能會感到比較陌生絮短,但說到斷點丁频,相信你就會聽著比較耳熟了邑贴。我們還以剛才我們提到的代碼為例:

斷點
斷點

我們在第 23 行左邊點擊了一下拢驾,就創(chuàng)建了一個斷點繁疤,這時我們再運行這個應用的時候稠腊,程序運行到這里就會被斷點攔截:

斷點
斷點

并且在 Xcode 的命令行區(qū)域架忌,顯示了 (lldb) 提示符。

基本調(diào)試操作

我們回到最初的問題饰恕,如果不使用 print 輸出埋嵌,我們怎么能得到 result 的值呢雹嗦,這就是我們要討論的斷點調(diào)試機制了俐银,我們先看一下 XCode 底部調(diào)試區(qū)域的幾個按鈕:

  • 第一個按鈕是繼續(xù)的意思捶惜,會讓程序從斷點處恢復吱七,繼續(xù)往下運行踊餐,我們點了這個按鈕后臀稚,應用就會恢復正常運行狀態(tài)吧寺。
  • 第二個按鈕是(Step Over),單步執(zhí)行的意思稚机,每點這個按鈕一次赖条,程序就會從我們斷點開始的地方纬乍,向下執(zhí)行一步。
  • 第三個按鈕是 (Step In)早芭,進入執(zhí)行的意思退个,簡單來說就是如果我們當前的斷點在一個函數(shù)調(diào)用上语盈,把么斷點會繼續(xù)進入這個函數(shù)的內(nèi)部進行調(diào)試刀荒。
  • 第四個按鈕是(Step Out),跳出的意思, 就是如果我們當前再一個函數(shù)中缠借,它會跳出當前的函數(shù)泼返,回到函數(shù)的調(diào)用處绅喉。

恩柴罐。革屠。你說了這么多,完全聽不懂啊

沒關系似芝,我們一一道來国觉,還是回到我們最初的需求麻诀,我們的斷點現(xiàn)在停在給 result 變量賦值的這條語句中蝇闭,斷點所在位置的語句是還沒有被執(zhí)行的呻引,所以我們需要點一下 Step Over 按鈕(也就是我們剛才列出的四個按鈕的第二個),讓程序執(zhí)行一行代碼元践。

執(zhí)行完這行代碼后单旁,我們的 result 變量就被賦值完成了象浑。那么問題來了绪穆,我們怎么得到 result 變量中得內(nèi)容呢?

還記得我們的 LLDB 命令行么藤乙,我們使用一個叫做 po 的命令,就可以取到這個變量:

現(xiàn)在饱苟,我們使用 LLDB 命令達到了和 print 語句同樣的效果,得到了 result 變量的取值狼渊。那么問題又來了箱熬,這樣做有什么好處呢,怎么感覺比直接使用 print 輸出更麻煩了呢狈邑?

下面我就來告訴大家原因城须。

LLDB 探索之旅

LLDB 為我們提供了很多方便使用的命令,我們再 LLDB 命令行中米苹,輸入 help 命令即可看到這些命令的幫助信息:

Debugger commands:

  apropos           -- Find a list of debugger commands related to a particular
                       word/subject.
  breakpoint        -- A set of commands for operating on breakpoints. Also see
                       _regexp-break.
  command           -- A set of commands for managing or customizing the
                       debugger commands.
  disassemble       -- Disassemble bytes in the current function, or elsewhere
                       in the executable program as specified by the user.
  expression        -- Evaluate an expression (ObjC++ or Swift) in the current
                       program context, using user defined variables and
                       variables currently in scope.
  frame             -- A set of commands for operating on the current thread's
                       frames.

  ...............

這里我們看到了 LLDB 命令的列表,要想獲得某個命令更詳細的幫助蘸嘶,我們開可以輸入 help 命令名, 比如我們輸入 help expression:

help expression
     Evaluate an expression (ObjC++ or Swift) in the current program context,
     using user defined variables and variables currently in scope.  This
     command takes 'raw' input (no need to quote stuff).

Syntax: expression <cmd-options> -- <expr>

Command Options Usage:
  expression [-AFLORTg] [-f <format>] [-G <gdb-format>] [-l <language>] [-a <boolean>] [-i <boolean>] [-t <unsigned-integer>] [-u <boolean>] [-v[<description-verbosity>]] [-d <none>] [-S <boolean>] [-D <count>] [-P <count>] [-Y[<count>]] [-V <boolean>] -- <expr>
  expression [-AFLORTg] [-l <language>] [-a <boolean>] [-i <boolean>] [-t <unsigned-integer>] [-u <boolean>] [-d <none>] [-S <boolean>] [-D <count>] [-P <count>] [-Y[<count>]] [-V <boolean>] -- <expr>

就得到了關于 expression 命令的介紹良瞧。

基本情況就說這么多,那么咱們就來實踐一下训唱,體驗一下 LLDB 的強大之處褥蚯。

我們來看一個更強大的命令 expression, 我們來看一下它的描述:

Evaluate an expression (ObjC++ or Swift) in the current program context,
using user defined variables and variables currently in scope. This
command takes 'raw' input (no need to quote stuff).

翻譯一下哈,意思就是在當前程序環(huán)境中况增,執(zhí)行任何的表達式赞庶,并且可以定義和操作已存在的變量。

怎么樣澳骤,讓我說的更具體吧歧强,有了 LLDB 我們不但可以在斷點處輸出某個變量的值,我們還可以修改甚至重新定義某些變量的值为肮。

咱們開始吧誊锭,將我們剛才的程序做一下修改:

var result = parseJSON("[1,2,3]");

if result?.count == 0 {

    print("No Data");

}else{

    print(result);

}

我們這里對 result 變量進行了判斷,并進行了分別的輸出弥锄。下面我們以讓將斷點設置到第一個語句上丧靡,然后運行程序蟆沫。再斷點處我們用 po 命令來打印出 result 的值。

這時候温治,result 中的值饭庞,應該是解析后的 JSON 數(shù)組。所以我們恢復程序執(zhí)行后熬荆,接下來的 if 判斷會走第二個分支舟山,輸出 result 中的內(nèi)容。

那么如果我們在剛才斷點時候卤恳,運行這個命令呢:

e result = []

這里我們將 result 的值修改為一個空數(shù)組累盗,然后我們繼續(xù)程序,接著你會發(fā)現(xiàn)突琳,下面的 if 判斷走了第一個分支若债,也就是說我們在斷點處對變量進行的修改,是對全局程序生效的拆融。

怎么樣這個能力是我們之前的調(diào)試方法不能達到的吧~

我們上面的 e 命令是 expression 命令的縮寫蠢琳,詳情可以參考 LLDB help 命令的幫助。

控制流快捷命令

我們繼續(xù)探索镜豹,還記得前面我們提到的幾個控制流按鈕嗎傲须,也就是這張圖片:

在 LLDB 命令行中,對于每個流程控制按鈕都有相應的命令趟脂。

  • n 命令泰讽,代表 Step Over 操作。
  • s 命令昔期,代表 Step Into 操作菇绵。
  • finish 命令,代表 Step Out 操作镇眷。
  • c 命令咬最,代表恢復程序執(zhí)行操作。

我們還是以這個程序為例欠动,這次我們使用控制流命令來進行操作:

我們運行這個程序永乌,然后在斷點檢測到時,按照下面的順序輸入 LLDB 命令:

s
n
n
c

我們第一個輸入的 s 命令具伍,會步入 parseJSON 函數(shù)的調(diào)用翅雏,然后斷點就會進入 parseJSON 函數(shù)中。隨后人芽,我們又輸入了 n 命令望几,由于 parseJSON 中只有一個 return 語句,那么控制流就會跳出 parseJSON 函數(shù)體萤厅,重新回到開始處橄抹。緊接著我們再次輸入 n 命令靴迫,這時候程序就會將 parseJSON 的結(jié)果賦值給 result。最后我們按下 c 命令楼誓,來恢復程序的執(zhí)行玉锌。隨后的 if 判斷中就會按照相應的條件輸出內(nèi)容了。

怎么樣疟羹,這樣操作起來就比較方便了主守,我們不必用鼠標點來點去了,完全用鍵盤敲命令就可以完成控制流的操作了榄融。

另外参淫,除了這些,還有一個更加實用的控制流語句 thread return愧杯。這個命令很有意思涎才,它不但可以使當前的函數(shù)返回,而且還可以任意修改當前函數(shù)的返回值民效,而不管傳進來的參數(shù)如何憔维。比如我們有這樣一個函數(shù):

func add(a:Int, b:Int) -> Int {

    return a + b;

}

如果斷點進入這個函數(shù)體的時候涛救,我們執(zhí)行了 thread return 3 命令畏邢,那么不管這時候傳進來的兩個參數(shù)是什么,這個函數(shù)都會退出執(zhí)行检吆,并返回我們指定的值 3舒萎。

恩。蹭沛。 這點還是有些神奇的~

斷點創(chuàng)建命令

我們除了通過用鼠標在代碼行的左邊點擊的方式創(chuàng)建斷點以外臂寝,我們還可以使用 LLDB 來創(chuàng)建斷點,比如要創(chuàng)建一個我們之前這樣的斷點:

我們可以輸入這樣一條命令:

(lldb) breakpoint set -f ViewController.swift -l 28
Breakpoint 2: where = Example`Example.ViewController.viewDidLoad (Example.ViewController)() -> () + 478 at ViewController.swift:29, address= 0x000000010f74f61e

輸入命令后摊灭,緊接著會有一行輸出咆贬,告訴我們斷點創(chuàng)建成功,并且顯示了創(chuàng)建的新斷點的位置等基本信息帚呼。

這個命令也有簡寫形式:

b ViewController.swift:28

我們還可以將斷點直接設置到函數(shù)上掏缎,假設我們有這樣一個函數(shù):

func add(a:Int, b:Int) -> Int {

    return a + b;

}

我們還可以這樣設置斷點:

b add

這樣就將斷點設置再了 add 函數(shù)調(diào)用的開始位置。

我們開可以設置符號斷點,比如這樣:

b -[NSArray objectAtIndex:]

這個斷點會將所有對于 NSArray 的 objectAtIndex 方法的調(diào)用設置為斷點煤杀。這里包括我們開發(fā)者對它的調(diào)用眷蜈,以及系統(tǒng)框架內(nèi)部對它的調(diào)用。符號斷點對于調(diào)試是一個很好用的工具沈自,它能夠跟蹤那些我們引用的系統(tǒng)庫中的代碼出現(xiàn)的問題酌儒。

我們還可以對已經(jīng)創(chuàng)建的斷點設置激發(fā)條件:

我們上面設置的條件表示,只有在 resultcount 屬性等于 0 的時候枯途,斷點才會被激發(fā)忌怎。

是不是覺得 LLDB 有點意思了呢籍滴。

剛才這一長串,給大家介紹了很多 LLDB 的基礎內(nèi)容呆躲,相信大家對 LLDB 已經(jīng)有了一個整體的了解异逐。

開始探險

那么現(xiàn)在我們就來用 LLDB 完成一些更加有意思的事情吧。

我們首先創(chuàng)建一個示例項目:

項目類型選擇 Single View Application

然后點擊 Next, 項目信息中的 Language 選擇 Swift:

點擊 Next 然后出現(xiàn)項目存儲位置的選擇插掂,選擇一個你自己的存儲位置灰瞻。接下來我們在 Main.storyboard 中拖放一個 Button 放到右上角:

隨后,我們按住 Option 鍵辅甥,然后點擊 ViewController.swift 文件酝润,可以在設計界面旁邊打開輔助界面。打開后璃弄,我們按住 Control 鍵要销,然后將我們剛剛創(chuàng)建的按鈕拖動到代碼視圖中:

然后松開鼠標按鍵,我們就會看到一個彈出菜單:

我們將 Connection 的類型選擇為 Action夏块, Name 輸入為 buttonClicked疏咐,其他不用更改,然后點擊 Connect 按鈕脐供。這樣就完成了按鈕事件的創(chuàng)建浑塞。

接下來,我們運行應用政己,就可以看到這樣的界面了:

一切就緒酌壕,現(xiàn)在就可以展開我們的 LLDB 大法啦~

其實我們還可以不通過斷點的方式來打開 LLDB 命令行,在我們先將程序運行起來歇由,然后我們看一下調(diào)試區(qū)域的按鈕:

注意下卵牍,藍色的斷點開關按鈕右邊還有一個暫停按鈕,我們只需要點這個暫停按鈕沦泌,就可以進入 LLDB 命令行調(diào)試狀態(tài)糊昙。因為 LLDB 在 Xcode 運行中是一直駐留在后臺的,所以我們其實是可以在任何時間都可以啟動 LLDB 命令行的谢谦。

打開 LLDB 命令行后释牺,我們可以輸入這個命令,打印出當前的視圖層級(又學一招~):

(lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]
<UIWindow: 0x7ffcd2f0f1e0; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x7ffcd2f10170>; layer = <UIWindowLayer: 0x7ffcd2f0ea80>>
   | <UIView: 0x7ffcd2c6dc10; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7ffcd2c17f10>>
   |    | <UIButton: 0x7ffcd2c6dfc0; frame = (20 62; 78 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7ffcd2c6bc10>>
   |    |    | <UIButtonLabel: 0x7ffcd2f15af0; frame = (16 6; 46 18); text = 'Button'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7ffcd2f16120>>
   |    | <_UILayoutGuide: 0x7ffcd2c6fae0; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x7ffcd2c6faa0>>
   |    | <_UILayoutGuide: 0x7ffcd2c70740; frame = (0 667; 0 0); hidden = YES; layer = <CALayer: 0x7ffcd2c6d3d0>>

大家仔細看一下他宛,每個視圖的標識中船侧,都有一個 16進制的字符串,代表這個視圖的 ID厅各,比如這個:

UIView: 0x7ffcd2c6dc10

這個 ID 的作用非常的強大镜撩,得到了這個 ID, 我們就可以通過這個命令來得到這個視圖的引用了:

(lldb) e id $view = (id) 0x7fbd71432590

簡單解釋下,通過 expression命令(這里用縮寫形式 e)袁梗,我們用 View 的 ID 值取得了這個 View 引用宜鸯,并將它保存到 $view 變量中。

我們得到了引用之后遮怜,就可以對這個視圖進行很多的操作了淋袖,比如我們可以在運行時改變這個視圖的背景色:

(lldb) e (void) [$view setBackgroundColor:[UIColor redColor]]

當然,我們運行完這條命令锯梁,界面上不會馬上反應出來即碗,我們還需要調(diào)用這個命令刷新一下:

(lldb) e (void)[CATransaction flush]

這樣,我們在看一下我們運行的程序陌凳,主界面的背景色變成紅色了吧剥懒。

我們甚至還可以用它來找到某些控件上添加的事件,我們找到我們自己添加的 UIBUtton 的 ID:

UIButton: 0x7ffcd2c6dfc0

然后運行下面的命令:

(lldb) e id $button = (id) 0x7ffcd2c6dfc0

(lldb) po [$button  allTargets]
{(
    <lldb.ViewController: 0x7feff2d67330>
)}

我們得到 UIButton 的引用后合敦,然后又輸出了他的 allTargets 屬性初橘,得到了這個 UIButton 所對應的事件 target 對象的地址,接下來我們再用剛剛得到的這個 target 地址獲取它的 action 屬性:

(lldb) po [$button actionsForTarget:(id)0x7feff2d67330 forControlEvent:0]
<__NSArrayM 0x7feff2c22350>(
buttonClicked:
)

我們這樣就得到了充岛,這個按鈕所對應的方法名了保檐。那么接下來,我們可以在這個方法上設置斷點崔梗,或者用 LLDB 的運行時能力替換這個方法的實現(xiàn)等等夜只。總之炒俱,對于我們調(diào)試應用來說盐肃,LLDB 是一個非常強大而高效的工具爪膊。這里只介紹了它的冰山一角权悟,關于更多的內(nèi)容,大家可以使用 help 命令推盛,進行深入的研究峦阁。相信大家發(fā)揮聰明才智,能夠發(fā)現(xiàn)更多它的強大之處耘成。

一點點延展榔昔,關于 Chisel

最后,再給大家延展一下瘪菌。LLDB 本身的命令系統(tǒng)非常健壯撒会,并且它還支持 Python 的腳本擴展,這樣它又有了很不錯的擴展性师妙,我們可以根據(jù)自己的需要來擴展自己的腳本诵肛。

Chisel 正是 LLDB 擴展的一個典型例子,這是由 Facebook 團隊開發(fā)的一個開源的 LLDB 的 Python 擴展集合默穴,它再 LLDB 命令的基礎上怔檩,又為我們提供了更加方便的操作接口褪秀。

比如我們要打印當前的視圖層級,如果用 LLDB 原生的命令薛训,我們需要這樣:

(lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]

而 Chisel 為我們提供了更簡潔的接口:

(lldb) pviews

同樣的媒吗,這條用于刷新顯示的命令:

(lldb) e (void)[CATransaction flush]

Chisel 也為我們提供了簡便的接口:

(lldb) caflush

這里只給大家做一個簡單的介紹,關于 Chisel 的更多內(nèi)容乙埃,大家可以參看它的主頁:https://github.com/facebook/chisel

LLDB 自身完善的命令行系統(tǒng)闸英,以及它的擴展能力,都成為提升我們開發(fā)效率的利器介袜。正確的使用好調(diào)試工具自阱,一定會幫助我們快速的解決更多的問題。

現(xiàn)在米酬,通過 help 命令沛豌,來開始對 LLDB 命令行的探索吧,相信你能在這里發(fā)現(xiàn)更多的寶藏赃额。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末加派,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子跳芳,更是在濱河造成了極大的恐慌芍锦,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件飞盆,死亡現(xiàn)場離奇詭異娄琉,居然都是意外死亡,警方通過查閱死者的電腦和手機吓歇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門孽水,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人城看,你說我怎么就攤上這事女气。” “怎么了测柠?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵炼鞠,是天一觀的道長。 經(jīng)常有香客問我轰胁,道長谒主,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任赃阀,我火速辦了婚禮霎肯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己姿现,他們只是感情好肠仪,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著备典,像睡著了一般异旧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上提佣,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天吮蛹,我揣著相機與錄音,去河邊找鬼拌屏。 笑死潮针,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的倚喂。 我是一名探鬼主播每篷,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼端圈!你這毒婦竟也來了焦读?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤舱权,失蹤者是張志新(化名)和其女友劉穎矗晃,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宴倍,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡张症,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了鸵贬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俗他。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖恭理,靈堂內(nèi)的尸體忽然破棺而出拯辙,到底是詐尸還是另有隱情郭变,我是刑警寧澤颜价,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站诉濒,受9級特大地震影響周伦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜未荒,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一专挪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦寨腔、人聲如沸速侈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽倚搬。三九已至,卻和暖如春乾蛤,著一層夾襖步出監(jiān)牢的瞬間每界,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工家卖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留眨层,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓上荡,卻偏偏與公主長得像趴樱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子酪捡,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 你是否曾經(jīng)苦惱于理解你的代碼伊佃,而去嘗試打印一個變量的值? NSLog(@"%@", whatIsInsideThi...
    木易林1閱讀 955評論 0 4
  • 你是否曾經(jīng)苦惱于理解你的代碼沛善,而去嘗試打印一個變量的值航揉? NSLog(@"%@", whatIsInsideThi...
    paraneaeee閱讀 1,192評論 0 7
  • 轉(zhuǎn)載 與調(diào)試器共舞 - LLDB 的華爾茲: https://objccn.io/issue-19-2/ 推薦:i...
    F麥子閱讀 3,332評論 0 10
  • 與調(diào)試器共舞 - LLDB 的華爾茲 nangege 2014/12/19 你是否曾經(jīng)苦惱于理解你的代碼,而去嘗試...
    McDan閱讀 881評論 0 0
  • 前言 今天花了一天的時間終于把iOS的幾種常見的調(diào)試方法給學習了一下金刁,在這里給大家分享一下LLDB的使用帅涂,同時也是...
    Peak_One閱讀 11,023評論 5 33