xcode各种调试手段

有6种方法,分别是:

NO1:最常见,也就是NSLog

从NSLog打印出的任何东西都会变成代码,任何人都可以看见。将设备连接到电脑,打开XCode中的organizer,就可以从console查看到每条日志信息,这会带来很大的影响。想一下,你想把一些保密的算法逻辑或者用户密码打印到console。正因为这个,如果苹果发现在项目发布中,有太多内容输出到console,那么你的应用可能会遭到苹果的拒绝。

幸运的是,这里有一个最简单的办法进行log——通过一个宏,让NSLog只在项目调试的时候起作用。将这个功能添加到全局都能访问得到的头文件中。这样你就可以尽情的使用log了,并且当进行项目发布时,不会包含log相关代码。如下代码:
 
1.#ifdef DEBUG
2.#define DMLog(...) NSLog(@"%s %@", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__])
3.#else
4.#define DMLog(...) do { } while (0)
 
如果你使用DMLog,那么它只能在项目调试期间打印。__PRETTY_FUNCTION__ 也可以帮忙打印出log所在的函数的名称。
另外在使用NSLog的时候应当注意,release版本中应该要去掉NSLog。
 

NO2:Enable NSZombie Objects (开启僵尸对象)--EXC_BAD_ACCESS

 NSZombieEnabled变量用来调试与内存有关的问题,跟踪对象的释放过程。启用了NSZombieEnabled的话,它会用一个僵尸来替换默认的dealloc实现,也就是在引用计数降到0时,该僵尸实现会将该对象转换成僵尸对象。僵尸对象的作用是在你向它发送消息时,它会显示一段日志并自动跳入调试器。

          所以,当在应用中启用NSZombie而不是让应用直接崩溃掉时,一个错误的内存访问就会变成一条无法识别的消息发送给僵尸对象。僵尸对象会显示接受到得信息,然后跳入调试器,这样你就可以查看到底是哪里出了问题。

         可以在Xcode的scheme页面中设置NSZombieEnabled环境变量。点击Product——>Edit Scheme打开该页面,然后勾选Enable Zombie Objects 复选框。

 

然后选中下图中的选项

 Zombie模式不能再真机上使用,只能在模拟器上使用

NO3:断点调试(全局断点、条件断点、符号断点)

一、全局断点(异常断点):异常断点可以快速定位不满足特定条件的异常,比如常见的数组越界,这时候很难通过异常信息定位到错误所在位置。这个时候异常断点就可以发挥作用了。

NSArray *aa = @[@2,@4];
NSLog(@"%@",aa[3]);

 这两行代码,没有添加全局断点时,运行crash,直接就跳到了mian函数,如下图:

接下来添加全局断点,方法如下图:

点击了Exception BreakPoint 后

添加之后运行,奔溃后,程序停留在了奔溃的那行代码。

 

Exception:可以选择抛出异常对象类型:OC或C++。

Break:选择断点接收的抛出异常来源是Throw还是Catch语句。

 

ps 不过有时候程序奔溃,这种方式定位不到

 

二、条件断点:设置断点触发的条件,方便开发者对特定情况进行调试

添加(删除)条件断点的快捷键:command+\

如下图:

在for循环中添加一个断点。右击断点选择”Edit BreakPoint”,然后设置断点触发条件。

这个例子当 “i==5”时,断点触发,如下图:

 

 

三、符号断点:

 

选择Symbolic Breakpoint后

例如:unrecognized selector send to instancd 的快速定位

NO4:

 

设置完成后再遇到类似的错误就会定位到具体的代码

NO4:Static Analyzer (静态分析)

Static Analyzer主要用于分析内存,避免内存泄漏。主要对以下情况进行分析。 
未使用的实例变量、未初始化的实例变量、类型不兼容、无法达到的路径、引用空指针 
使用快捷键:command + shift +B,可以去如下图中去设置

如下图就能轻松找到可能内存泄漏的代码,然后我们根据代码环境进行修复就可以了

 

(ps:有的内存泄漏可能检测不出来,还是需要我们在写代码时对内存这块多留点心。)

 

NO5:Address Sanitizer

在Xcode7之后新增了AddressSanitizer工具,为我们调试EXC_BAD_ACCESS错误提供了便利。当程序创建变量分配一段内存时,将此内存后面的一段内存也冻结住,标识为中毒内存。程序访问到中毒内存时(访问越界),立即中断程序,抛出异常并打印异常信息。你可以根据中断位置及输出的Log信息来解决错误。当然,如果变量已经释放了,它所占用的内存也会被标识为中毒内存,这个时候访问这片内存空间同样会抛出异常。

使用方法:

“Edit Scheme…” —> “Run” —> “Diagnostics” —> “Zombie Objects”

 

 

开启AddressSanitizer之后,在调试程序的过程中,如果有遇到EXC_BAD_ACCESS错误,程序则会自动终止,抛出异常。

 

NO6:LLDB调试器

LLDB是一个有着 REPL 的特性和 C++ ,Python 插件的开源调试器。LLDB 绑定在 Xcode 内部,存在于主窗口底部的控制台中。调试器允许你在程序运行的特定时暂停它,你可以查看变量的值,执行自定的指令,并且按照你所认为合适的步骤来操作程序的进展。(这里有一个关于调试器如何工作的总体的解释。)

你以前有可能已经使用过调试器,即使只是在 Xcode 的界面上加一些断点。但是通过一些小的技巧,你就可以做一些非常酷的事情。GDB to LLDB 参考是一个非常好的调试器可用命令的总览。你也可以安装 Chisel,它是一个开源的 LLDB 插件合辑,这会使调试变得更加有趣。

与此同时,让我们以在调试器中打印变量来开始我们的旅程吧。

基础

这里有一个简单的小程序,它会打印一个字符串。注意断点已经被加在第 8 行。断点可以通过点击 Xcode 的源码窗口的侧边槽进行创建。

1.png

程序会在这一行停止运行,并且控制台会被打开,允许我们和调试器交互。那我们应该打些什么呢?

help

最简单命令是 help,它会列举出所有的命令。如果你忘记了一个命令是做什么的,或者想知道更多的话,你可以通过 help来了解更多细节,例如 help print 或者 help thread。如果你甚至忘记了 help 命令是做什么的,你可以试试 help help。不过你如果知道这么做,那就说明你大概还没有忘光这个命令。??

print

打印值很简单;只要试试 print 命令:

2.png

LLDB 实际上会作前缀匹配。所以你也可以使用 prin,pri,或者 p。但你不能使用 pr,因为 LLDB 不能消除和 process 的歧义 (幸运的是 p 并没有歧义)。

你可能还注意到了,结果中有个 $0。实际上你可以使用它来指向这个结果。试试 print $0 + 7,你会看到 106。任何以美元符开头的东西都是存在于 LLDB 的命名空间的,它们是为了帮助你进行调试而存在的。

expression

如果想改变一个值怎么办?你或许会猜 modify。其实这时候我们要用到的是 expression 这个方便的命令。

这不仅会改变调试器中的值,实际上它改变了程序中的值。这时候继续执行程序,将会打印 42 red balloons。神奇吧。

注意,从现在开始,我们将会偷懒分别以 p 和 e 来代替 print 和 expression。

什么是 print 命令

考虑一个有意思的表达式:p count = 18。如果我们运行这条命令,然后打印 count 的内容。我们将看到它的结果与 expression count = 18 一样。

和 expression 不同的是,print 命令不需要参数。比如 e -h +17 中,你很难区分到底是以 -h 为标识,仅仅执行 +17 呢,还是要计算 17 和 h 的差值。连字符号确实很让人困惑,你或许得不到自己想要的结果。

幸运的是,解决方案很简单。用 -- 来表征标识的结束,以及输入的开始。如果想要 -h 作为标识,就用 e -h -- +17,如果想计算它们的差值,就使用 e -- -h +17。因为一般来说不使用标识的情况比较多,所以 e -- 就有了一个简写的方式,那就是 print。

输入 help print,然后向下滚动,你会发现:

1
2
'print' is an abbreviation for 'expression --'.   
(print是 `expression --` 的缩写)

打印对象

尝试输入

1
p objects

输出会有点啰嗦

1
(NSString *) $7 = 0x0000000104da4040 @"red balloons"

如果我们尝试打印结构更复杂的对象,结果甚至会更糟

1
2
(lldb) p @[ @"foo", @"bar" ]
(NSArray *) $8 = 0x00007fdb9b71b3e0 @"2 objects"

实际上,我们想看的是对象的 description 方法的结果。我么需要使用 -O (字母 O,而不是数字 0) 标志告诉 expression 命令以 对象 (Object) 的方式来打印结果。

1
2
3
4
(lldb) e -O -- $8(
foo,
bar
)

幸运的是,e -o -- 有也有个别名,那就是 po (print object 的缩写),我们可以使用它来进行简化:

1
2
3
4
5
6
7
8
(lldb) po $8(
foo,
bar
)
(lldb) po @"lunar"
lunar
(lldb) p @"lunar"
(NSString *) $13 = 0x00007fdb9d0003b0 @"lunar"

打印变量

可以给 print 指定不同的打印格式。它们都是以 print/或者简化的 p/格式书写。下面是一些例子:

默认的格式

1
2
(lldb) p 16
16

十六进制:

1
2
(lldb) p/x 16
0x10

二进制 (t 代表 two):

1
2
3
4
(lldb) p/t 16
0b00000000000000000000000000010000
(lldb) p/t (char)16
0b00010000

你也可以使用 p/c 打印字符,或者 p/s 打印以空终止的字符串 (译者注:以 '\0' 结尾的字符串)。 

这里是格式的完整清单。

变量

现在你已经可以打印对象和简单类型,并且知道如何使用 expression 命令在调试器中修改它们了。现在让我们使用一些变量来减少输入量。就像你可以在 C 语言中用 int a = 0 来声明一个变量一样,你也可以在 LLDB 中做同样的事情。不过为了能使用声明的变量,变量必须以美元符开头。

1
2
3
4
5
6
7
8
9
10
11
(lldb) e int $a = 2
(lldb) p $a * 19
38
(lldb) e NSArray *$array = @[ @"Saturday", @"Sunday", @"Monday" ]
(lldb) p [$array count]
2
(lldb) po [[$array objectAtIndex:0] uppercaseString]
SATURDAY
(lldb) p [[$array objectAtIndex:$a] characterAtIndex:0]
error: no known method '-characterAtIndex:'; cast the message send to the method's return type
error: 1 errors parsing expression

悲剧了,LLDB 无法确定涉及的类型 (译者注:返回的类型)。这种事情常常发生,给个说明就好了:

1
2
3
4
(lldb) p (char)[[$array objectAtIndex:$a] characterAtIndex:0]
'M'
(lldb) p/d (char)[[$array objectAtIndex:$a] characterAtIndex:0]
77

变量使调试器变的容易使用得多,想不到吧???

bt

打印调用堆栈信息,使用 bt all命令可以打印出所有thread的调用栈信息

比如说:call [self.view setBackgroundColor:[UIColor redColor]],使用这个命令来设置view controller的背景色为红色

 

对中文的兼容

有中文时必须使用 [NSString stringWithUTF8String:] 方法,原因在于lldb的expression parser有一个bug,不兼容非ASCII字符,需要处理一下才行,否则会报错“An Objective-C constant string's string initializer is not an array”,参考:http://stackoverflow.com/questions/17192505/error-in-breakpoint-condition

类型的问题

出现类型不确定或类型不匹配的时候,就会报错,这个时候必须强制转换才可以。

比如前文中获取indexPath的row的方法的表达式:(int)[indexPath row],或者类似下面这种干脆无返回值 p (void)NSLog(@"%@",[self.view  viewWithTag:1001]),或者上文中的 p (char)[[$array objectAtIndex:$a] characterAtIndex:0],这些场景中都需要明确输出的类型。

找不到方法

注意,使用系统框架对象的属性时不能使用dot语法,比如下图中的问题。

blob.png

blob.png改成如下的格式才行:

blob.png

流程控制

当你通过 Xcode 的源码编辑器的侧边槽 (或者通过下面的方法) 插入一个断点,程序到达断点时会就会停止运行。

调试条上会出现四个你可以用来控制程序的执行流程的按钮。

4.png

从左到右,四个按钮分别是:continue,step over,step into,step out。

第一个,continue 按钮,会取消程序的暂停,允许程序正常执行 (要么一直执行下去,要么到达下一个断点)。在 LLDB 中,你可以使用 process continue 命令来达到同样的效果,它的别名为 continue,或者也可以缩写为 c。

第二个,step over 按钮,会以黑盒的方式执行一行代码。如果所在这行代码是一个函数调用,那么就不会跳进这个函数,而是会执行这个函数,然后继续。LLDB 则可以使用 thread step-over,next,或者 n 命令。

如果你确实想跳进一个函数调用来调试或者检查程序的执行情况,那就用第三个按钮,step in,或者在LLDB中使用 thread step in,step,或者 s 命令。注意,当前行不是函数调用时,next 和 step 效果是一样的。

大多数人知道 c,n 和 s,但是其实还有第四个按钮,step out。如果你曾经不小心跳进一个函数,但实际上你想跳过它,常见的反应是重复的运行 n 直到函数返回。其实这种情况,step out 按钮是你的救世主。它会继续执行到下一个返回语句 (直到一个堆栈帧结束) 然后再次停止。

例子

考虑下面一段程序:

5.png

假如我们运行程序,让它停止在断点,然后执行下面一些列命令:

1
2
3
4
5
6
7
p i
n
s
p i
finish
p i
frame info

这里,frame info 会告诉你当前的行数和源码文件,以及其他一些信息;查看 help frame,help thread 和 help process 来获得更多信息。这一串命令的结果会是什么?看答案之前请先想一想。

1
2
3
4
5
6
7
8
9
10
11
12
13
(lldb) p i
(int) $0 = 99
(lldb) n
2014-11-22 10:49:26.445 DebuggerDance[60182:4832768] 101 is odd!
(lldb) s
(lldb) p i
(int) $2 = 110
(lldb) finish
2014-11-22 10:49:35.978 DebuggerDance[60182:4832768] 110 is even!
(lldb) p i
(int) $4 = 99
(lldb) frame info
frame #0: 0x000000010a53bcd4 DebuggerDance`main + 68 at main.m:17

它始终在 17 行的原因是 finish 命令一直运行到 isEven() 函数的 return,然后立刻停止。注意即使它还在 17 行,其实这行已经被执行过了。

Thread Return

调试时,还有一个很棒的函数可以用来控制程序流程:thread return 。它有一个可选参数,在执行时它会把可选参数加载进返回寄存器里,然后立刻执行返回命令,跳出当前栈帧。这意味这函数剩余的部分不会被执行。这会给 ARC 的引用计数造成一些问题,或者会使函数内的清理部分失效。但是在函数的开头执行这个命令,是个非常好的隔离这个函数,伪造返回值的方式 。

让我们稍微修改一下上面代码段并运行:

1
2
3
4
5
6
p i
s
thread return NO
n
p even0
frame info

看答案前思考一下。下面是答案:

1
2
3
4
5
6
7
8
9
(lldb) p i
(int) $0 = 99
(lldb) s
(lldb) thread return NO
(lldb) n
(lldb) p even0
(BOOL) $2 = NO
(lldb) frame info
frame #0: 0x00000001009a5cc4 DebuggerDance`main + 52 at main.m:17

 

 

Chisel-LLDB命令插件

Chisel介绍

源码地址:Chisel
Chisel扩展了一些列的lldb的命令来帮助iOS开发者调试iOS应用程序。

安装Chisel

1.确保终端安装了Homebrew

2.终端执行命令:brew install chisel  输入命令后我遇到第一个问题。


brew install chisel 后可能出现的问题

碰见这个问题终端执行命令:sudo chown -R ${USER} /Library/Caches/Homebrew/,执行此命令后问题解决。

3.如果没有第二步的问题,执行命令:brew install chisel后出现如下界面


brew install chisel 执行成功

4.注意看Caveats下面的那两行,意思是把第二行的文字command script import /usr/local/opt/chisel/libexec/fblldb.py添加到.lldbinit文件中,这时执行命令echo command script import /usr/local/opt/chisel/libexec/fblldb.py >> ~/.lldbinit(粗体文字替换为你终端Caveats下面的第二行文字)可免去你去找.lldbinit文件,或者.lldbinit文件不出现的烦恼啊。到此步不出意外已经安装成功。

5.安装成功后重新启动Xcode即可。

6.终端下检查是否安装成功输入命令:lldb,然后输入help,往下翻出现如下界面为成功


Chisel安装成功

7.Xcode下检查是否安装成功,打断点进入lldb下输入help,往下翻出现如下界面为成功


Chisel安装成功后,就开始熟悉命令

2.内置命令

Chisel 为lldb提供了新增的便捷命令,是非常实用的命令

2.1 pviews

这个命令可以递归打印所有的view,并能标示层级,相当于 UIView 的私有辅助方法  [view recursiveDescription] 。 善用使用这个功能会让你在调试定位问题时省去很多麻烦。

使用示例:

(lldb) pviews view
<TestView: 0x18df8070; baseClass = UIControl; frame = (144 9; 126 167); layer = <CALayer: 0x18df8150>>
   | <UIView: 0x18df81d0; frame = (0 0; 126 126); userInteractionEnabled = NO; layer = <CALayer: 0x18df8240>>
   | <UIImageView: 0x18df8330; frame = (0 0; 126 126); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x18df83b0>>
   | <UILabel: 0x18df8460; frame = (0 135; 126 14); text = 'haha'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x18df7fb0>>
   |    | <_UILabelContentLayer: 0x131a3d50> (layer)
   | <UILabel: 0x18df8670; frame = (0 155; 126 12); text = 'hahaha'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x18df8730>>
   |    | <_UILabelContentLayer: 0x131bea10> (layer)
   | <UIImageView: 0x18df88d0; frame = (0 9; 28 27); hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x18df8ba0>>

 

2.2 pvc

这个命令也是递归打印层级,但是不是view,而是viewController。利用它我们可以对viewController的结构一目了然。 其实苹果在IOS8也默默的添加了 UIViewController 的一个私有辅助方法 [UIViewController _printHierarchy] 同样的效果。

预览效果:

(lldb) pvc
<TabBarController: 0x13772fd0; view = <UILayoutContainerView; 0x151b3a30>; frame = (0, 0; 414, 736)>
   | <UINavigationController: 0x1602b800; view = <UILayoutContainerView; 0x1b00aca0>; frame = (0, 0; 414, 736)>
   |   | <FirstViewController: 0x16029c00; view = <UIView; 0x1b01e1c0>; frame = (0, 0; 414, 736)>
   | <UINavigationController: 0x138c5200; view = <UILayoutContainerView; 0x1316a080>; frame = (0, 0; 414, 736)>
   |   | <SecondViewController: 0x16030400; view = <UIView; 0x2094b370>; frame = (0, 0; 414, 736)>
   |   |   | <SecondChildViewController: 0x15af6000; view = <UIView; 0x18d4e650>; frame = (0, 64; 414, 628)>
   | <UINavigationController: 0x1383ca00; view = <UILayoutContainerView; 0x13180070>; frame = (0, 0; 414, 736)>
   |   | <ThirdViewController: 0x138ddc00; view = <UIView; 0x18df6650>; frame = (0, 0; 414, 736)>
   |   |   | <ThirdChild1ViewController: 0x1393fe00; view = <UIView; 0x131ec000>; frame = (0, 0; 414, 672)>
   |   |   | <ThirdChild2ViewController: 0x138dce00; view = <UIView; 0x204075a0>; frame = (414, 0; 414, 672)>
   |   |   | <ThirdChild3ViewController: 0x138a8e00; view = <UIView; 0x20426250>; frame = (828, 0; 414, 672)>
   | <UINavigationController: 0x160eca00; view = <UILayoutContainerView; 0x152f7d90>; frame = (0, 0; 414, 736)>
   |   | <FourViewController: 0x13157cc0; view not loaded>

 

是不是方便很多呢,而且还可以看到 viewController 是否已经 viewDidLoad .

2.3 visualize

这是个很有意思的功能,它可以让你使用Mac的预览打开一个 UIImage, CGImageRef, UIView, 或 CALayer。 这个功能或许可以帮我们用来截图、用来定位一个view的具体内容。 但是在我试用了一下,发现暂时还是只能在模拟器时使用,真机还不行。

使用简单:

(lldb) visualize imageView

 

2.4 fv & fvc

fv 和 fvc 这两个命令是用来通过类名搜索当前内存中存在的view和viewController实例的命令,支持正则搜索。

如:

(lldb) fv scrollView
0x18d3b8c0 UIScrollView
0x137d0c50 UIScrollView
0x131b1580 UIScrollView
0x131b2070 UIScrollView
(lldb) fvc Home
0x1393fe00 HomeFeedsViewController
0x138a8e00 HomeFeedsViewController
(lldb)

 

2.5 show & hide

这两个命令用来显示和隐藏一个指定的 UIView . 

   (lldb) show self.view
   (lldb) hide  self.view

 也可以使用内存地址隐藏和现实view,比如通过 fv cate找到一个view后使用hide隐藏它

(lldb)fv cate
    0x7fd5b6e06920 AlbumCategoryView

 (lldb) hide  0x7fd5b6e06920

 

2.6 mask/umask border/unborder

这两组命令用来标识一个view或layer的位置时用, mask用来在view上覆盖一个半透明的矩形, border可以给view添加边框。但是在我实际使用的过程中mask总是会报错,估计是有bug, 那么mask/unmask 一般不要用好了,用border命令是一样的效果,反正二者的用途都是找到一个对应的view.

如果我要给第二个view添加一个颜色为蓝色,宽度为2的边框,之后再用unborder命令移除,操作如下:

通过help border命令知道border的使用格式如下:

Options:
--color/-c <color>; Type: string; A color name such as 'red', 'green',
 'magenta', etc.
 --width/-w <width>; Type: CGFloat; Desired width of border.
Syntax: border [--color=color] [--width=width] <viewOrLayer>

 

其中viewOrLayer表示你要修改的view的地址,我们通过pviews命令知道,第二个view的地址是0x7feae2d605f0,所以我们输入

border -c blue -w 2 0x7feae2d605f0  //添加边框
unborder 0x7feae2d605f0   //移除边框

 注意我在输入每个border/unborder命令时,右侧模拟器第二个view(红色view)的变化。

2.7 caflush

这个命令会重新渲染,即可以重新绘制界面, 相当于执行了 [CATransaction flush] 方法,要注意如果在动画过程中执行这个命令,就直接渲染出动画结束的效果。

当你想在调试界面颜色、坐标之类的时候,可以直接在控制台修改属性,然后caflush就可以看到效果啦,是不是要比改代码,然后重新build省事多了呢。

例, 其中 $122 即是目标UIView:

(lldb) p view
(long) $122 = 140718754142192
(lldb) e (void)[$122 setBackgroundColor:[UIColor greenColor]]
(lldb) caflush

 

2.8 bmessage

这个命令就是用来打断点用的了,虽然大家断点可能都喜欢在图形界面里面打,但是考虑一种情况:我们想在 [MyViewController viewWillAppear:] 里面打断点,但是 MyViewController并没有实现 viewWillAppear: 方法, 以往的作法可能就是在子类中实现下viewWillAppear:,然后打断点,然后rebuild。

那么幸好有了 bmessage命令。我们可以不用这样就可以打这个效果的断点: (lldb) bmessage -[MyViewController viewWillAppear:] 上面命令会在其父类的 viewWillAppear: 方法中打断点,并添加上了条件:[self isKindOfClass:[MyViewController class]]

3. 自定义命令

我们也可以自定义插件,不过前提是要懂一些 python。 比如设计一个打印keyWindow的windowLevel的命令:

创建python脚本文件 /magical/commands/example.py :

#!/usr/bin/python
# Example file with custom commands, located at /magical/commands/example.py

import lldb
import fblldbbase as fb

def lldbcommands():
  return [ PrintKeyWindowLevel() ]

class PrintKeyWindowLevel(fb.FBCommand):
  def name(self):
    return 'pkeywinlevel'

  def description(self):
    return 'An incredibly contrived command that prints the window level of the key window.'

  def run(self, arguments, options):
    # It's a good habit to explicitly cast the type of all return
    # values and arguments. LLDB can't always find them on its own.
    lldb.debugger.HandleCommand('p (CGFloat)[(id)[(id)[UIApplication sharedApplication] keyWindow] windowLevel]')

 

其中定义了PrintKeyWindowLevel的类,需要实现 name description run 方法来分别告诉名称、描述、和执行实体。

创建好脚本后,然后在前面安装时创建的 ~/.lldbinit文件中添加一行:

script fblldb.loadCommandsInDirectory('/magical/commands/')

 

然后重启Xcode之后就可以使用自定义的命令啦。

 

 

参考链接:

http://www.jianshu.com/p/79468a2eb6db

https://blog.cnbluebox.com/blog/2015/03/05/chisel/

http://www.cocoachina.com/ios/20141219/10709.html

http://www.jianshu.com/p/3e4b10083b4d

http://www.cocoachina.com/ios/20150803/12805.html

http://www.cocoachina.com/ios/20161102/17884.html

http://blog.csdn.net/u013822374/article/details/50963108

http://www.cocoachina.com/ios/20130517/6225.html

http://www.cocoachina.com/ios/20140526/8555.html

 

posted on 2017-04-15 15:42  7月前减到115斤  阅读(846)  评论(0)    收藏  举报