Jekyll2022-02-23T02:14:19+00:00http://klone.space/atom.xmlklone1127生命在于折腾klone1127Chapter 5 - Expression2017-09-15T00:00:00+00:002017-09-15T00:00:00+00:00http://klone.space/%E6%8A%80%E6%9C%AF/2017/09/15/Expression<blockquote>
<blockquote>
<p>以下内容整理自:<a href="https://store.raywenderlich.com/products/advanced-apple-debugging-and-reverse-engineering"><ADVANCED APPLE DEBUGGING & REVERSE ENGINEERING></a> 如有出入望指正</p>
</blockquote>
</blockquote>
<blockquote>
<p>本节主要讲通过 <strong>expression</strong> 命令对代码进行调试</p>
</blockquote>
<h3 id="formatting-p--po">Formatting p & po</h3>
<p><strong>po</strong> 经常用于 Swift & Objective-C 的一些输出操作。</p>
<p>如果你使用 <strong>help po</strong> 查看的话,你会发现 <strong>po</strong> 是 <strong>expression -O –</strong> 的缩写,<strong>O</strong> 用于输出对象的描述信息。</p>
<p><strong>po</strong> 的一个经常被忽略的姐妹,<strong>p</strong>,是一个省去了__O__ 的表达式(<strong>expression -O –</strong>)。p 输出格式更多的依赖于 <strong>LLDB type system</strong>。LLDB 的值类型格式有助于确定其输出,并可以完全自定义(像你将在下面看到的那样)。</p>
<p>现在是时候学习 p&po 命令如何获取内容的时候了。这此节将继续使用 Signals 项目。</p>
<p>打开 <strong>MasterViewController.swift</strong> ,添加如下代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> override var description: String {
return "Yay! debugging " + super.description
}
</code></pre></div></div>
<p>在 <strong>viewDidLoad</strong> 方法中,添加代码到 <strong>super.viewDidLoad()</strong> 下面:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print("\(self)")
</code></pre></div></div>
<p>添加断点到 print 方法下边,像下面这样,编译运行:</p>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/2017-09-15-Expression-add-breakpoint?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" /></p>
<p>停到 viewDidLoad() 之后,输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) po self
Yay! debugging <Signals.MasterViewController: 0x7f8335f0db10>
</code></pre></div></div>
<p>记下 print 语句的输出以及它如何匹配的你刚刚在调试器中执行的 po self。</p>
<p>你也可以进一步,NSObject 有另一种方法没描述用于调试称为 <strong>debugDescription</strong>。现在试着实现。在刚添加 description 变量下面添加:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> override var debugDescription: String {
return "debugDescription: " + super.debugDescription
}
</code></pre></div></div>
<p>编译运行,这时在断点处停下后输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) po self
</code></pre></div></div>
<p>控制台输出与下面类似:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>debugDescription: Yay! debugging <Signals.MasterViewController: 0x7f9b27c0df80>
</code></pre></div></div>
<p>注意在你执行了 debugDescription 之后,po self 和 print 输出的 self 的不同。使用 LLDB 打印对象的时候,调用 debugDescription 方法,而不是 description。</p>
<p>你可以看到,在使用 NSObject 类或者子类的时,<code class="language-plaintext highlighter-rouge">description</code> 或者 <code class="language-plaintext highlighter-rouge">debugDescription</code> 将影响 <code class="language-plaintext highlighter-rouge">po</code> 的输出。</p>
<p>那么哪些对象重写了这些描述方法?可以使用 <code class="language-plaintext highlighter-rouge">image lookup</code> 查看。</p>
<p>举例来说,如果你想知道所有重写 <code class="language-plaintext highlighter-rouge">debugDescription</code> 的 Objective-C 的类,可以使用:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) image lookup -rn '\ debugDescription\]'
</code></pre></div></div>
<p>基于输出,Foundation 框架的开发者将 <code class="language-plaintext highlighter-rouge">debugDescription</code> 添加到了很多基础类中(i.e. NSArray),这样方面于我们调试。此外,也有私有类重写了 <code class="language-plaintext highlighter-rouge">debugDescription</code> 方法。</p>
<p>在输出中可以看到有 CALayer。让我们看看 <code class="language-plaintext highlighter-rouge">description</code> 和 <code class="language-plaintext highlighter-rouge">debugDescription</code> 在 CALayer 中的不同。</p>
<p>输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) po self.view.layer.description
</code></pre></div></div>
<p>输出类似信息:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"<CALayer: 0x600000034f60>"
</code></pre></div></div>
<p>信息太少了,换个方式:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) po self.view.layer
</code></pre></div></div>
<p>得到类似以下输出:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><CALayer:0x600000034f60; position = CGPoint (187.5 333.5); bounds = CGRect (0 0; 375 667); delegate = <UITableView: 0x7fc2d501a000; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x600000244b90>; layer = <CALayer: 0x600000034f60>; contentOffset: {0, 0}; contentSize: {600, 0}; adjustedContentInset: {0, 0, 0, 0}>; sublayers = (<CALayer: 0x600000035480>, <CALayer: 0x600000035520>); masksToBounds = YES; allowsGroupOpacity = YES; backgroundColor = <CGColor 0x6000000b8d80> [<CGColorSpace 0x6000000b8780> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1; extended range)] ( 0.980392 0.980392 0.980392 1 )>
</code></pre></div></div>
<p>输出很多很有用的信息,很显然, Core Animation 的开发人员决定 <code class="language-plaintext highlighter-rouge">description</code> 应该只是对象引用,但如果你在调试器中,你可以看到更多的信息。不清楚为什么要造成这种差异。可能是为了节省资源。</p>
<p>下一步,如果你仍停在调试器(没有的话,回到 viewDidLoad() 断点),尝试在 <code class="language-plaintext highlighter-rouge">self</code> 上运行 <code class="language-plaintext highlighter-rouge">p</code> 命令</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) p self
</code></pre></div></div>
<p>得到类似以下输出:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(Signals.MasterViewController) $R3 = 0x00007fc2d4c0b080 {
UIKit.UITableViewController = {
baseUIViewController@0 = <extracting data from value failed>
_tableViewStyle = 0
_keyboardSupport = nil
_staticDataSource = nil
_filteredDataSource = 0x0000608000247d70
_filteredDataType = 0
}
detailViewController = nil
}
</code></pre></div></div>
<p>看起来有点恐怖,让我们慢慢讲。</p>
<p>首先,LLDB 输出 self 的类名。类名:Signals.MasterViewController</p>
<p>然后是一个引用,你可以在 LLDB 会话中引用这个对象。栗子中的是 <code class="language-plaintext highlighter-rouge">$R0</code>,这个数字会在你使用 LLDB 的时候增加。</p>
<p>如果你想在之后的会话中使用这个对象,或许当你处于不同的范围并且 <code class="language-plaintext highlighter-rouge">self</code> 不再是同一个对象,这个引用将非常有用。这种情况下,你可以使用 <code class="language-plaintext highlighter-rouge">$R0</code> 使用这个对象。使用方法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) p $R0
</code></pre></div></div>
<p>你可以看到同样信息又输出了一边。</p>
<p>在 LLDB 变量名称之后是该对象的地址,后面是一些特定于此类类型的输出。在这种情况下,它显示 MasterViewController 的父类 UITableViewController 的相关信息,后跟 detailViewController 实例变量。</p>
<p>p 的输出取决于 <strong>type formatting</strong>,LLDB 开发人员已经在内部数据结构中添加了所有的(受关注的)数据结构,Objective-C、 Swift 还有一些其他的语言。尤其要注意的是, Swift 处在开发阶段,所以 MasterViewController 输出可以有所不同。</p>
<p>由于这些类型类型格式化程序是由 LLDB 持有的,所以如果愿意,可以直接更改的。在 LLDB 会话中,输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) type summary add Signals.MasterViewController --summary-string "哇喔"
</code></pre></div></div>
<p>你告诉 LLDB 当输出 MasterViewController 实例的时候你只想输出字符串 “哇喔”。输出 self:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) p self
(Signals.MasterViewController) $R0 = 0x00007fdd377029e0 哇喔
</code></pre></div></div>
<p>这个样式会被 LLDB 记录,下次启动的时候还存在,需要在使用结束之后清除掉。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) type summary clear
</code></pre></div></div>
<p>现在输入 <code class="language-plaintext highlighter-rouge">p self</code> 就回到默认输出样式了。</p>
<h3 id="swift-vs-objective-c-debugging-contexts">Swift vs Objective-C debugging contexts</h3>
<p>需要注意的是调试程序时有两个调试上下文:非 Swift 调试上下文和 Swift 上下文。默认情况下,当停在 Objective-C 代码时,LLDB 使用非 Swift(Objective-C) 调试上下文,而在 Swift 代码中停止时, LLDB 将使用 Swift上下文。听起来很合逻辑,对吧?</p>
<p>如果你意外的停止,LLDB 将默认选择 Objective-C 上下文。</p>
<p>确保刚才的蓝色(GUI 下设置)断点还在,编译运行。停在断点处后,在LLDB 会话中输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) po [UIApplication sharedApplication]
</code></pre></div></div>
<p>LLDB 会抛出一个奇怪的错误:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>error: <EXPR>:3:16: error: expected ',' separator
[UIApplication sharedApplication]
^
,
</code></pre></div></div>
<p>你这时是停在 Swift 代码中,所以是 Swift 上下文,而且你执行的是 Objective-C 代码。因此行不通。同样的,在 Objective-C 上下文 po Swift 多象也是不可以的。</p>
<p>在 Objective-C 上下文时可以在 expression 中使用 <code class="language-plaintext highlighter-rouge">-l</code> 来选择语言。然而,由于 <code class="language-plaintext highlighter-rouge">po</code> 是 <code class="language-plaintext highlighter-rouge">expression -O --</code> 的映射,所以你不能直接在 <code class="language-plaintext highlighter-rouge">po</code> 后面直接跟 <code class="language-plaintext highlighter-rouge">--</code>,这意味着需要使用 <code class="language-plaintext highlighter-rouge">expression</code>。在 LLDB,输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) expression -l objc -O -- [UIApplication sharedApplication]
</code></pre></div></div>
<p>这时你告诉 LLDB 为 Objective-C 使用 <code class="language-plaintext highlighter-rouge">objc</code>,如果有需要,还可以为 Objective-C++ 使用 <code class="language-plaintext highlighter-rouge">objc++</code>。</p>
<p>使用 Swift 做同样的事情:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) po UIApplication.shared
</code></pre></div></div>
<p>和使用 Objective-C 输出的相同,然后 continue,之后再突然停止。</p>
<p>按 <code class="language-plaintext highlighter-rouge">↑</code>,看到:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) po UIApplication.shared
</code></pre></div></div>
<p>这时看输出:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>error: property 'shared' not found on object of type 'UIApplication'
</code></pre></div></div>
<p>请记住,LLDB 意外停止时使用的是 Objective-C 上下文,这就是为什么执行 Swift 方法出现错误的原因。</p>
<p>你应该始终了解当前在调试器中暂停的位置使用的语言。</p>
<h3 id="user-defined-variables">User defined variables</h3>
<p>如之前看到的,当打印对象时,LLDB 将自动创建本地变量。你也可以创建自己的变量。</p>
<p>移除所有断点,运行应用。在调试器意外的停止,默认是 Objective-C 上下文。输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) po id test = [NSObject new]
</code></pre></div></div>
<p>LLDB 将会执行这个代码,该代码创建一个新的 NSObject 并将其存储到 test 变量中。现在打印出来:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) po $test
出现一个错误:
error: use of undeclared identifier 'test'
</code></pre></div></div>
<p>这是因为你需要使用 <code class="language-plaintext highlighter-rouge">$</code> 预先添加要使用 LLDB 的变量。</p>
<p>重新输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) po id $test = [NSObject new]
(lldb) po $test
<NSObject: 0x604000014eb0>
</code></pre></div></div>
<p>此变量在 Objective-C 对象中创建。但是,如果你尝试从 Swift 上下文访问会发生什么?试一下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) expression -l swift -O -- $test
</code></pre></div></div>
<p>发现输出还是正常的。现在尝试使用 Swift 代码风格在这个 Objective-C 上执行。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) expression -l swift -O -- $test.description
</code></pre></div></div>
<p>输出错误:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>error: <EXPR>:3:1: error: use of unresolved identifier '$test'
$test.description
^~~~~
</code></pre></div></div>
<p>如果在 Objective-C 上下文中创建一个 LLDB 变量,然后使用 Swift 上下文,不要指望一切可以“正常运行”。目前这块在积极开发,Objective-C 和 Swift 桥接可能会随着时间的推移而改善。</p>
<p>那么如何在 LLDB 中创建引用可以在实际情况下使用?你可以获取对象的引用,并执行(以及调试)任意选择的方法。要查看此操作,创建一个符号断点在 MasterViewController 的父控制器 __MasterContainerViewController__上,为 MasterContainerViewController 的 viewDidLoad 添加符号断点。</p>
<p>在 Symbol 行输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Signals.MasterContainerViewController.viewDidLoad() -> ()
</code></pre></div></div>
<p>输完之后如下:</p>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/2017-10-12-Symbolic-Breakpoint-MasterContainerViewController-viewDidLoad?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" /></p>
<p>编译运行,Xcode 将停在 MasterContainerViewController.viewDidLoad()。输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) p self
</code></pre></div></div>
<p>由于这是在 Swift 上下文中执行的第一个参数,所以 LLDB 将会创建变量 <code class="language-plaintext highlighter-rouge">$R0</code>。通过在 LLDB 中执行 continue 恢复程序的执行。</p>
<p>现在你没有通过使用 self 来引用 MasterContainerViewController 的实例,因为执行已经不在 viewDidLoad(),并移动到 bigger and better run loop events。</p>
<p>你这时候还有 <code class="language-plaintext highlighter-rouge">$R0</code> 变量,你可以引用 MasterContainerViewController,甚至是执行任意方法来帮助调试代码。</p>
<p>手动暂定调试的应用,然后输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) po $R0.title
</code></pre></div></div>
<p>很不幸,报错了:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>error: use of undeclared identifier '$R0'
</code></pre></div></div>
<p>因为这时候调试器是意外停止的。记住,LLDB 默认的是 Objective-C;你需要使用 <code class="language-plaintext highlighter-rouge">-l</code> 来保证使用的是 Swift 上下文;</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) expression -l swift -- $R0.title
</code></pre></div></div>
<p>这时候看下输出:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(String?) $R1 = "Quarterback"
</code></pre></div></div>
<p>当然,这是 view Controller 的标题,如导航栏显示。</p>
<p>现在输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) expression -l swift -- $R0.title = "😛😛😛😛"
</code></pre></div></div>
<p>然后 <code class="language-plaintext highlighter-rouge">continue</code> 恢复运行。</p>
<blockquote>
<p><strong>Note:</strong> 在 macOS 上使用 <code class="language-plaintext highlighter-rouge">⌘ + ⌃(control) + 空格</code> 可以进行符号和表情的选择。</p>
</blockquote>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/2017-10-13-lldb-change-navigationItemTitle?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" /></p>
<p>此外,你也可以在代码中创建断点,执行代码,命中断点。如果你正在调试并希望通过某些输入来查看其操作方式,这很可能会有用。</p>
<p>例如:你符号断点仍然在 viewDidLoad() 方法上,请尝试执行该方法来检查代码。暂停执行程序,然后输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) expression -l swift -O -- $R0.viewDidLoad()
</code></pre></div></div>
<p>什么也没有发生,断点也没有出现。是什么原因呢?实际上,MasterContainViewController 已经执行了这个方法。但是默认情况下,LLDB 会在执行命令是忽略任何断点。你可以使用 <code class="language-plaintext highlighter-rouge">-i</code> 禁用此选项。</p>
<p>在 LLDB 会话输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) expression -l swift -O -i 0 -- $R0.viewDidLoad()
</code></pre></div></div>
<p>LLDB 现在在之前创建的 viewDidLoad() 符号断点中断。这种策略是测试逻辑的好方法。例如,你可以通过给出不同参数的函数来实现 test-driven 调试。以了解如何处理不同的输入。</p>
<h3 id="type-formatting">Type formatting</h3>
<p>编译运行,然后暂停,确保你停在 Objective-C 上下文。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) expression -G x -- 10
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">-G</code> 告诉 <strong>LLDB</strong> 输出想要使用的格式。<strong>G</strong> 表示 <strong>GDB</strong>。GDB 是 LLDB 之前的调试器。所以,这时你指定了使用 GDB 格式。在这种情况下, <code class="language-plaintext highlighter-rouge">x</code> 表示十六进制。</p>
<p>输出结果:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(int) $0 = 0x0000000a
</code></pre></div></div>
<p>这是十进制10的十六进制。</p>
<p>还有更多!LLDB 允许你使用简洁语法格式。输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) p/x 10
(int) $1 = 0x0000000a
</code></pre></div></div>
<p>你会发现一样的输出。但是输入少了很多。</p>
<p>这对学习 C 数据类型很有帮助。例如,10的二进制数据?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) p/t 10
(int) $2 = 0b00000000000000000000000000001010
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">/t</code> 指定了二进制格式。你可以看到10的二进制显示。例如:当处理一个位字段是很有用,以便自习检查给定数字将设置哪些字段。</p>
<p>-10 的二进制是多少?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) p/t -10
(int) $3 = 0b11111111111111111111111111110110
</code></pre></div></div>
<p>10.0的二进制呢?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) p/t 10.0
(double) $4 = 0b0100000000100100000000000000000000000000000000000000000000000000
</code></pre></div></div>
<p>‘D’ 的 <strong>ASCII</strong> 是多少?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) p/d 'D'
(char) $5 = 68
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">/d</code> 指定的是十进制格式。</p>
<p>最后,下面这个整数隐藏的缩写是什么?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) p/c 1430672467
(int) $7 = STFU
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">/c</code> 指定的是字符串格式。它采用二进制数,分为8位(1 字节),并将每个块转换为 <strong>ASCII</strong> 字符。在这种情况下,它是一个4字符的代码(FourCC),结果是 <code class="language-plaintext highlighter-rouge">SDFU</code>。</p>
<p>完整的输出格式列表(<a href="https://sourceware.org/gdb/ onlinedocs/gdb/Output-Formats.html">https://sourceware.org/gdb/ onlinedocs/gdb/Output-Formats.html</a>)</p>
<ul>
<li>x: 十六进制(hexadecimal)</li>
<li>d: 十进制(decimal)</li>
<li>u: 无符号十进制(unsigned decimal)</li>
<li>o: 八进制(octal)</li>
<li>t: 二进制(binary)</li>
<li>a: 地址(address)</li>
<li>c: 字符常数(character constant)</li>
<li>f: 浮点数(float)</li>
<li>s: 字符串(string)</li>
</ul>
<p>如果这些格式不够用,还可以使用 LLDB 的额外的格式化方式,尽管将不能使用 GDB 格式化语法。</p>
<p>LLDB 的格式化方法可以像以下方式使用:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) expression -f Y -- 1430672467
(int) $0 = 53 54 46 55 STFU
</code></pre></div></div>
<p>这解释了之前的 FourCC 代码。</p>
<p>LLDB 有以下格式化程序(<a href="http://lldb.llvm.org/ varformats.html">http://lldb.llvm.org/ varformats.html</a>):</p>
<ul>
<li>B: boolean</li>
<li>b: binary</li>
<li>y: bytes</li>
<li>Y: bytes with ASCII</li>
<li>c: character</li>
<li>C: printable character</li>
<li>F: complex float</li>
<li>s: c-string</li>
<li>i: decimal</li>
<li>E: enumeration</li>
<li>x: hex</li>
<li>f: float</li>
<li>o: octal</li>
<li>O: OSType</li>
<li>U: unicode16</li>
<li>u: unsigned decimal</li>
<li>p: pointer</li>
</ul>
<h3 id="where-to-go-from-here">Where to go from here?</h3>
<p>思考一下可以用表达式命令做什么。尝试通过执行 <code class="language-plaintext highlighter-rouge">help expression</code> 自己探索一些其他的表达式选项,并查看是否可以弄清楚它们在做什么。</p>klone1127以下内容整理自:<ADVANCED APPLE DEBUGGING & REVERSE ENGINEERING> 如有出入望指正Chapter 4 - Stopping in Code2017-09-08T00:00:00+00:002017-09-08T00:00:00+00:00http://klone.space/%E6%8A%80%E6%9C%AF/2017/09/08/Stopping-in-Code<blockquote>
<blockquote>
<p>以下内容整理自:<a href="https://store.raywenderlich.com/products/advanced-apple-debugging-and-reverse-engineering"><ADVANCED APPLE DEBUGGING & REVERSE ENGINEERING></a> 如有出入望指正</p>
</blockquote>
</blockquote>
<blockquote>
<p>不管你是使用 Swif, Objective-C, C++,C 或是其他语言,你都将学会使用断点。在图形界面的 Xcode 中点击面板边上就可以很轻松的设置一个断点,但是使用 LLDB 控制台可以带来更多控制方式的断点。</p>
</blockquote>
<blockquote>
<p><a href="http://7xq3rg.com1.z0.glb.clouddn.com/AdvanceAppleDebugging_v0.9.1_SourceCode.zip">随书的代码资源下载</a>(我放七牛上的资源),也可以<a href="https://wolverine.raywenderlich.com/print-books/dbg/AdvanceAppleDebugging_v0.9.1_SourceCode.zip">下载官网</a></p>
</blockquote>
<h3 id="signal">Signal</h3>
<p>在这节中,可以使用随书的项目;名字是 <strong>Signal</strong>。<strong>Signal</strong> 是一个 <strong>basic master-detail</strong> 项目,这个项目可以几个 <strong>Unix 信号</strong>,并且可以在接收到的时候显示出来。</p>
<p><strong>Unix 信号</strong> 是系统中线程间通讯的一种有限制的方式。比如,有一个信号 <strong>SIGSTOP</strong>,用以保存状态、暂停进程执行,当收到 <strong>SIGCONT</strong> 时,会让程序重新开始执行。这两个信号可以用于 <strong>debugger</strong> 暂停 和继续运行程序。</p>
<p>这是程序在几个方面挺有趣的,因为它不仅探究 Unix 信号处理,而且当控制进程(LLDB)处理通过 Unix 信号传递到受控进程时可以高亮显示。默认情况下,LLDB 具有处理不同信号的自定义操作。在 LLDB attach 的时候,有些信号是不会受到控制。</p>
<p>想要显示一个信号,你可以在应用内 raise 一个信号,也可以从其他应用发送一个信号,如终端。</p>
<p>此外,有一个 UISwitch 触发信号处理,会调用 C 函数 sigprocmask 响应不可用或者可用信号处理。</p>
<p>最后,Signal 程序有一个 <strong>Timeout</strong> 按钮可以在应用内唤起 SIGSTOP 信号,其实就是暂停程序。然而,如果 LLDB 已经 attach Signals(在你用 Xcode 编译运行的时候默认会的),在 Xcode 中使用 LLDB 唤起 SIGSTOP 就可以检查执行程序状态。</p>
<blockquote>
<blockquote>
<p>这几处原文</p>
</blockquote>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Unix signals are a basic form of interprocess communication. For example, one of the signals, SIGSTOP, can be used to save the state and pause execution of a process, while its counterpart, SIGCONT, is sent to a program to resume execution. Both of these signals can be used by a debugger to pause and continue a program’s execution.
This is an interesting application on several fronts, because it not only explores Unix signal handling, but also highlights what happens when a controlling process (LLDB) handles the passing of Unix signals to the controlled process. By default, LLDB has custom actions for handling different signals. Some signals are not passed onto the controlled process while LLDB is attached.
In order to display a signal, you can either raise a Signal from within the application, or send a signal externally from a different application, like Terminal.
In addition, there’s a UISwitch that toggles the signal handling blocking, which calls the C function sigprocmask to disable or enable the signal handlers.
Finally, the Signal application has a Timeout bar button which raises the SIGSTOP signal from within the application, essentially “freezing” the program. However, if LLDB is attached to the Signals program (and by default it will be, when you build and run through Xcode), calling SIGSTOP will allow you to inspect the execution state with LLDB while in Xcode.
</code></pre></div></div>
<p>选择 <strong>iPhone 7 Simulator</strong> 作为测试机,编译运行程序。程序运行后,点击暂停</p>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/2017-09-15-Stopping-in-Code-Xcode?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="400px" /></p>
<p>恢复运行,看一下模拟器。这时你会看到产生一条新的内容,这是被 Signals 监听的 Unix 信号被触发后添加到数据模型中的。当一个进程停止时,任何新的信号将不会被立即处理,因为程序在一定程度上停止了(the program is sort of, well, stopped.)。</p>
<h3 id="xcode-breakpoints">Xcode breakpoints</h3>
<p><strong>Symbolic breakpoints</strong> 在 Xcode 中非常好的 debugging feature。在应用中可以添加包含 <strong>Sympbol</strong> 的断点。举个 -[NSObject init] 的栗子,NSObject 实例的 init 方法。</p>
<p>比较优雅的事情是,在 Xcode 中添加一个symbolic breakpoint后,你在下次启动应用时不必重新输入。</p>
<p>现在可以使用 symbolic breakpoint 来显示所有正在创建的 NSObject 实例。</p>
<p>如果应用正在运行请关闭,切换到 <strong>Breakpoint Naivgator(⌘ + 8)</strong>,在左下方,点击 <strong>+</strong>,选择 <strong>Symbolic Breakpoint…</strong>。</p>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/2017-09-15-symbolic-breakpoint?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="400px" /></p>
<p>这时会有一个弹窗出现,在 <strong>Symbol</strong> 中输入 <strong>-[NSObject init]</strong>。下边的 <strong>Action</strong>,选择 __ Add Action__ 然后下拉选择 <strong>debugger Command</strong>。</p>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/2017-09-15-symbolic-breakpoint-set?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="400px" /></p>
<p>编译运行,Xcode 将会打印所有运行时创建的类名,然后,,,会看到很多很多</p>
<p>你已经创建了一个每次运行 -[NSObject init] 都会被触发的断点。当断点触发,命令在 LLDB 种运行,程序继续执行。</p>
<p><strong>Note</strong>: 在第10章节可以知道 “Assembly, Registers and Calling Convention”,但现在,只要知道 <strong>$arg1</strong> 和 <strong>$rdi</strong> 作用相同就行,当 init 调用时,可以保存类的实例。</p>
<p>当你观察结束,可以symbolic breakpoint断点,对着断点右键,选择 <strong>Delete Breakpoint</strong>。</p>
<p>除了 symbolic breakpoint 之外,Xcode 还提供了其他的类型的断点,有个 <strong>Exception Breakpoint</strong>,程序出错的时候或者crash,这时设置一个 <strong>Exception Breakpoint</strong>,在程序抛出异常的时候会被触发。Xcode 会显示哪一行的问题,这极大的帮助了寻找crash的根源。</p>
<p>最后,是 <strong>Swift Error Breakpoint</strong>,在任何时候,Swift 都会通过在 swift_willThrow 方法创建一个断点来抛出后错误。如果你正在使用易于出错的API,这是一个很好的选择,因为它可以让你快速诊断出情况,而不会让你对正确的代码做出错误的假设。</p>
<h3 id="lldb-breakpoint-syntax">LLDB breakpoint syntax</h3>
<p>现在你已经对Xcode的调试功能有了一定的了解,可以开始学习通过 LLDB 控制台创建断点了。为了创建有用的断点,你需要学习如何查询你想要查询的内容。</p>
<p><strong>image</strong> 命令是一个很好的工具,可以帮助内省细节,这对设置断点至关重要。</p>
<p>在书中你将会有两种用于代码搜索的配置。第一种就是以下方法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) image lookup -n "-[UIViewController viewDidLoad]"
</code></pre></div></div>
<p>这个命令输出 -[UIViewController viewDidLoad] 加载的地址, -n 参数告诉 LLDB 查找符号或者函数名。类似输出:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1 match found in /Users/mylove/Downloads/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/UIKit.framework/UIKit:
Address: UIKit[0x00000000001c76db] (UIKit.__TEXT.__text + 1858059)
Summary: UIKit`-[UIViewController viewDidLoad]
</code></pre></div></div>
<p>另一个有用且类似的命令是:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) image lookup -rn test
</code></pre></div></div>
<p>通过区分大小写的正则来查找 “test”。如果这个小写 “test 在任何位置,任何函数,任何模块(i.e. UIKit, Foundation, Core Data, etc)中加载到当前可执行文件(不会从 release builds中移除。。。稍后再说),这个命令会输出结果。</p>
<p><strong>Note</strong>: 当你需要精确匹配的时候,使用 -n 参数(如果包含空格,则可以使用引号包住),并用 -rn 参数进行正则表达式搜索。-n 命令有助于匹配断点的确切参数,尤其是处理 Swift的时候,而 -rn 参数将会非常受到青睐,因为一个巧妙的正则表达式就可以避免打很多字。</p>
<h3 id="objective-c-properties">Objective-C properties</h3>
<p>学习如何查询加载的代码对于学习如何在该代码上创建断点至关重要。Swift 和 Objective-C 的属性签名在它们被编译器创建已经生成了,这就导致了不同的断点策略。</p>
<p>举例来说,以下是 Objective-C 在 Signals 中声明的属性:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@interface TestClass : NSObject
@property (nonatomic, strong) NSString *name;
@end
</code></pre></div></div>
<p>编译器会为 name 生成 setter 和 getter 方法。
getter 方法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-[TestClass name]
</code></pre></div></div>
<p>setter 方法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-[TestClass setName:]
</code></pre></div></div>
<p>编译运行项目,然后暂定调试,通过 LLDB 验证方法是否存在:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) image lookup -n "-[TestClass name]"
1 match found in /Users/mylove/Library/Developer/Xcode/DerivedData/Signals-fdzrglclifcqooeqhnyaxxvmqakl/Build/Products/Debug-iphonesimulator/Signals.app/Signals:
Address: Signals[0x0000000100001cb0] (Signals.__TEXT.__text + 0)
Summary: Signals`-[TestClass name] at TestClass.h:28
</code></pre></div></div>
<p>LLDB 将输出包含在可执行文件中的函数信息。输出的信息看起来有点吓人,但这里有一些很好的信息。</p>
<p>控制台输出可以得出信息,LLDB 可以在 Signals 可执行文件中发现这个方法实现,在TEXT中的 0x0000000100001470偏移量是准确的,LLDB 也指明这个方法是在 TestClass.h 文件中的28行声明。</p>
<p>你也可以查找 setter 方法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) image lookup -n "-[TestClass setName:]"
1 match found in /Users/mylove/Library/Developer/Xcode/DerivedData/Signals-fdzrglclifcqooeqhnyaxxvmqakl/Build/Products/Debug-iphonesimulator/Signals.app/Signals:
Address: Signals[0x0000000100001cd0] (Signals.__TEXT.__text + 32)
Summary: Signals`-[TestClass setName:] at TestClass.h:28
</code></pre></div></div>
<h3 id="swift-properties">Swift properties</h3>
<p>属性在 Swift 中的语法就有很大不同了,可以看下 SwiftTestClass.swift:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class SwiftTestClass: NSObject {
var name: String!
}
</code></pre></div></div>
<p>确认 <strong>Signals</strong> 是运行的,并且处于 LLDB 状态,<strong>⌘ + k</strong> 清空控制台信息。</p>
<p>在 LLDB 控制台,输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) image lookup -rn Signals.SwiftTestClass.name.setter
</code></pre></div></div>
<p>输出类似信息:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2 matches found in /Users/mylove/Library/Developer/Xcode/DerivedData/Signals-fdzrglclifcqooeqhnyaxxvmqakl/Build/Products/Debug-iphonesimulator/Signals.app/Signals:
Address: Signals[0x000000010000caf0] (Signals.__TEXT.__text + 44608)
Summary: Signals`@objc Signals.SwiftTestClass.name.setter : Swift.ImplicitlyUnwrappedOptional<Swift.String> at SwiftTestClass.swift Address: Signals[0x000000010000cbb0] (Signals.__TEXT.__text + 44800)
Summary: Signals`Signals.SwiftTestClass.name.setter : Swift.ImplicitlyUnwrappedOptional<Swift.String> at SwiftTestClass.swift:28
</code></pre></div></div>
<p>在输出内容中查找 <strong>Summary</strong> 。这里是成对输出的。</p>
<p>首先,发现有两个 symbols。第一个和第二个同名;然而,第一个开头有个 @objc,这是一个特殊的函数是由编译器加上去的,作为 bridging 功能。有助于 Swift 和 Objective-C 混编。</p>
<p>其次,你有没有发现函数名有多长?!设置一个Swift断点需要写出所有东西,!如果你想在 setter 上设置一个断点,你需要写以下类似内容:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) b Signals.SwiftTestClass.name.setter : Swift.ImplicitlyUnwrappedOptional<Swift.String>
</code></pre></div></div>
<p>使用正则表达式是一个有吸引力的替代这种残暴输入的方法。</p>
<p>除了生成的Swift函数长度之外,注意Swift属性的生成。函数名后跟着属性name,之后跟着setter,也许这也适用于getter?</p>
<p>使用以下方法同时搜索 SwiftTestClass 属性name的 setter 和 getter 方法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) image lookup -rn Signals.SwiftTestClass.name.
</code></pre></div></div>
<p>使用正则表达式查找所有包含 Signals.SwiftTestClass.name. 的内容。</p>
<p>输出类似以下内容:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>6 matches found in /Users/mylove/Library/Developer/Xcode/DerivedData/Signals-fdzrglclifcqooeqhnyaxxvmqakl/Build/Products/Debug-iphonesimulator/Signals.app/Signals:
Address: Signals[0x000000010000c980] (Signals.__TEXT.__text + 44240)
Summary: Signals`@objc Signals.SwiftTestClass.name.getter : Swift.ImplicitlyUnwrappedOptional<Swift.String> at SwiftTestClass.swift Address: Signals[0x000000010000ca40] (Signals.__TEXT.__text + 44432)
Summary: Signals`Signals.SwiftTestClass.name.getter : Swift.ImplicitlyUnwrappedOptional<Swift.String> at SwiftTestClass.swift:28 Address: Signals[0x000000010000caf0] (Signals.__TEXT.__text + 44608)
Summary: Signals`@objc Signals.SwiftTestClass.name.setter : Swift.ImplicitlyUnwrappedOptional<Swift.String> at SwiftTestClass.swift Address: Signals[0x000000010000cbb0] (Signals.__TEXT.__text + 44800)
Summary: Signals`Signals.SwiftTestClass.name.setter : Swift.ImplicitlyUnwrappedOptional<Swift.String> at SwiftTestClass.swift:28 Address: Signals[0x000000010000ccc0] (Signals.__TEXT.__text + 45072)
Summary: Signals`closure #1 : () in Signals.SwiftTestClass.name.materializeForSet : Swift.ImplicitlyUnwrappedOptional<Swift.String> at SwiftTestClass.swift Address: Signals[0x000000010000ccd0] (Signals.__TEXT.__text + 45088)
Summary: Signals`Signals.SwiftTestClass.name.materializeForSet : Swift.ImplicitlyUnwrappedOptional<Swift.String> at SwiftTestClass.swift
</code></pre></div></div>
<p>重点看 <strong>Summary</strong> ,你可以看到 setter 和 getter 方法,materializeForSet 方法。</p>
<p>这里有一个Swift属性模板:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ModuleName.ClassName.PropertyName.(getter|setter)
</code></pre></div></div>
<h3 id="finally-creating-breakpoints">Finally… creating breakpoints</h3>
<p>现在你已经知道在怎么在代码中查询函数和方法是否存在了,是时候开始给它们下断点了。</p>
<p>创建断点的方法有几种。<strong>b</strong> 是最基本的一种。这在Objective-C和C中相当容易,因为名字很短,容易输入(e.g. -[NSObject init])。这在C++和Swift中是相当棘手的,因为编译器将你的方法转成名字很长的符号。</p>
<p>由于UIKit主要由Objective-C编写(至少书中原文撰写的时候),尝试使用 b 创建一个断点,像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) b -[UIViewController viewDidLoad]
</code></pre></div></div>
<p>类似这样的输出:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Breakpoint 4: where = UIKit`-[UIViewController viewDidLoad], address = 0x000000010ce426db
</code></pre></div></div>
<p><strong>Breakpoint 4:</strong> 说明我这是创建的第四个断点,多个的时候自增。</p>
<p>断点创建好了之后,继续运行Xcode,这时Signals上会增加一个新的cell,点击cell查看详情,刚设置的断点会在加载 viewDidLoad 方法的时候被触发。</p>
<p><strong>Note:</strong> 像很多简写命令一样,b 是另一个长的 LLDB 命令的缩写。尝试运行 help</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) help b
Set a breakpoint using one of several shorthand formats.
Expects 'raw' input (see 'help raw-input'.)
Syntax: b
'b' is an abbreviation for '_regexp-break'
</code></pre></div></div>
<p>除了 b 命令,还有一个有一些选项的 breakpoint set 命令。</p>
<h3 id="regex-breakpoints-and-scope">Regex breakpoints and scope</h3>
<p>另一个非常强大的命令是正则表达式断点 <strong>rbreak</strong>,它是 <strong>breakpoint set -r %1</strong> 的缩写。</p>
<p>回到上一个例子,Swift有很长的属性名:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) b Signals.SwiftTestClass.name.setter : Swift.ImplicitlyUnwrappedOptional<Swift.String> >这里需要指出,书中写的是:
(lldb) b Breakpoints.SwiftTestClass.name.setter :
Swift.ImplicitlyUnwrappedOptional<Swift.String> 使用书中的方法好像没什么作用。
</code></pre></div></div>
<p>简单设置断点:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) rb SwiftTestClass.name.setter
</code></pre></div></div>
<p>尽管这个方法简单,但也是有点烦恼的。这个断点会捕获两个 setter,包括 Objective-C 桥接文件在内的方法。</p>
<p>你可以添加 ^(@).* 到断点上来扩充断点,基本上可以说“不用让函数以@开头了”,在以后的章节中,你将构建执行正则表达式的可以自动过滤掉桥接函数的命令。</p>
<p>现在你只需要处理两个断点。更简短,可以用下面的命令:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) rb name\.setter
</code></pre></div></div>
<p>这个命令会产生一些包含 name.setter 的断点。如果你肯定你项目中没有其他名为name的属性,这将很有用;否则你将为它们创建多个断点。</p>
<p>现在尝试为所有Objective-C 的UIViewController 设置断点,在 LLDB 会话中执行:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) rb '\-\[UIViewController\ '
Breakpoint 5: 762 locations.
</code></pre></div></div>
<p>使用 <em>__</em> 转义, 表示你希望文字在正则表达式中搜索。结果就是,所有包含’-[UIViewController ‘(添加’‘是为了指出此处有空格)的方法都设置了断点。</p>
<table>
<tbody>
<tr>
<td>稍等…关于Objective-C 的扩展呢?它们的格式为 (-</td>
<td>+)[ClassName(categoryName) method]。你必须重写正则表达式以包括类别。</td>
</tr>
</tbody>
</table>
<p>删除所有断点,输入以下内容,并在提示后直接 Enter/输入y:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) breakpoint delete
About to delete all breakpoints, do you want to do that?: [Y/n]
</code></pre></div></div>
<p>下一步,输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) rb '\-\[UIViewController(\(\w+\))?\ '
Breakpoint 6: 928 locations.
</code></pre></div></div>
<p>在断点中UIViewController后可以有一个带有一个或多个字符的括号。</p>
<p>使用正则表达式断点,你可以使用单个表达式捕获各种各样的断点。</p>
<p>使用 -f 参数,可以将断点的范围限制在某一个文件中。例如,像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) rb . -f DetailViewController.swift
</code></pre></div></div>
<p>当你调试DetailViewController.swift的时候会被触发。它将在这个文件中的所有属性的getters/setters,block/closures,extensions/categories,和 函数/方法 上添加断点。-f 常被用作范围限制。</p>
<p>如果你想更疯狂的尝试,可以去掉范围限制:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) rb .
</code></pre></div></div>
<p>它将在任何地方添加断点…包括Signals所有代码,使用到的UIKit和Foundation,所有run loop 事件代码,</p>
<p>你也可以使用__-s__限制在一个library中:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) rb . Commons
</code></pre></div></div>
<p>这将在Commons库中的所有内容上设置一个断点,它是Signalals项目中包含的一个动态库。</p>
<p>你可以使用相同的方法对在UIKit上添加断点。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) rb . -s UIKit
</code></pre></div></div>
<p>只停止在你点的第一个包含在UIKit中的方法上怎么样?<strong>-o</strong> 参数可以解决,它创建了“one-shot”断点。断点会在被命中后删除。所以它只会命中一次。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) breakpoint delete
(lldb) rb . -s UIKit -o
</code></pre></div></div>
<p><strong>Note:</strong> 输入命令后需要等一会儿,因为这时需要设置很多很多断点。请确认你使用的是模拟器,否则你将等待更长时间。</p>
<p>下一步,继续调试,点击模拟器上的cell。这时停在触发的第一个UIKit的方法上,然后 continue,将不会再触发断点。</p>
<h3 id="modifying-and-removing-breakpoints">Modifying and removing breakpoints</h3>
<p>现在你已经基本可以创建断点了,你可能会想知道怎么更改。如果你想删除断点或者暂时禁用,怎么办?如果你需要修改断点以在下次触发时执行特定操作,该怎么办?</p>
<p>首先,你需要发现断点的唯一标示,或者一组断点。你可以使用 <strong>-N</strong> 在创建时给断点命名…</p>
<p>输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) b main
Breakpoint 1: 44 locations.
</code></pre></div></div>
<p>这个命令会在44个位置创建断点,与各个模块中的 “main” 功能匹配。</p>
<p>breakpoint ID 是1,因为这是在这个会话中创建的第一个断点。要想看这个断点的详细信息可以输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) breakpoint list 1
</code></pre></div></div>
<p>会得到类似输出:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Current breakpoints:
1: name = 'main', locations = 44, resolved = 44, hit count = 0
1.1: where = Signals`main + 4 at AppDelegate.swift:28, address = 0x0000000103ee8544, resolved, hit count = 0
1.2: where = Foundation`-[NSThread main], address = 0x00000001042309e3, resolved, hit count = 0
1.3: where = Foundation`-[NSBlockOperation main], address = 0x000000010423d7d6, resolved, hit count = 0
1.4: where = Foundation`-[NSFilesystemItemRemoveOperation main], address = 0x0000000104276e99, resolved, hit count = 0
1.5: where = Foundation`-[NSFilesystemItemMoveOperation main], address = 0x00000001042779ee, resolved, hit count = 0
...
</code></pre></div></div>
<p>更清晰的查看方法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) breakpoint list 1 -b
1: name = 'main', locations = 44, resolved = 44, hit count = 0
</code></pre></div></div>
<p>这个输出简明扼要,如果你有大量断点,可以使用这个方法。</p>
<p>如果你想查看所有断点:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) breakpoint list
</code></pre></div></div>
<p>也可以根据ID和范围查看:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) breakpoint list 1 3
(lldb) breakpoint list 1-3
</code></pre></div></div>
<p>可以单独删除一个断点:</p>
<blockquote>
<blockquote>
<p>根据ID
(lldb) breakpoint delete 1</p>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<p>也可以根据位置进行删除</p>
</blockquote>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) breakpoint delete 1.1
</code></pre></div></div>
<p>最后这个之后删除第一个断点的第一个子断点。</p>
<blockquote>
<h3 id="扩展阅读">扩展阅读</h3>
</blockquote>
<ul>
<li><a href="https://zh.wikipedia.org/wiki/Unix%E4%BF%A1%E5%8F%B7">Unix 信号-维基百科</a></li>
<li><a href="https://docs.python.org/2/library/re.html">python 正则表达式</a></li>
</ul>klone1127以下内容整理自:<ADVANCED APPLE DEBUGGING & REVERSE ENGINEERING> 如有出入望指正Chapter 3 - Attaching with LLDB2017-09-06T00:00:00+00:002017-09-06T00:00:00+00:00http://klone.space/%E6%8A%80%E6%9C%AF/2017/09/06/Attaching-With-LLDB<blockquote>
<blockquote>
<p>以下内容整理自:<a href="https://store.raywenderlich.com/products/advanced-apple-debugging-and-reverse-engineering"><ADVANCED APPLE DEBUGGING & REVERSE ENGINEERING></a> 如有出入望指正</p>
</blockquote>
</blockquote>
<blockquote>
<p>本节就是讲各种 attach 应用的方法</p>
</blockquote>
<p>放上介绍的更详细的原文参考:</p>
<blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The phrase of LLDB “attaching” is actually a bit misleading. A program named debugserver (found in Xcode.app/Contents/SharedFrameworks/LLDB.framework/ Resources/) is responsible for attaching to a target process.
If it’s a remote process, such as an iOS, watchOS or tvOS application running on a remote device, a remote debugserver gets launched on that remote device. It’s LLDB’s job to launch, connect, and coordinate with the debugserver to handle all the interactions in debugging an application.
</code></pre></div> </div>
</blockquote>
<h3 id="attaching-to-an-existing-process">Attaching to an existing process</h3>
<blockquote>
<p>已经运行的进程可以使用以下方法:</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lldb -n 有道词典
</code></pre></div></div>
<p>通过名字直接捕获,这里会有个问题,如果你遇到可以双开的程序会给出提示:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mylove@klonedeMacBook-Pro~]]]$lldb -n QQ
(lldb) process attach --name "QQ"
error: attach failed: more than one process named QQ:
PID PARENT USER TRIPLE ARGUMENTS
====== ====== ========== ======================== ============================
647 1 mylove x86_64-apple-macosx /Applications/QQ.app/Contents/MacOS/QQ -psn_0_192559
303 1 mylove x86_64-apple-macosx /Applications/QQ.app/Contents/MacOS/QQ -psn_0_49164
(lldb)
</code></pre></div></div>
<p>这时候你可以通过 PID 捕获</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lldb -p 647
</code></pre></div></div>
<p>获取程序 PID 可以在终端输入</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$pgrep -x QQ
303
647
</code></pre></div></div>
<p>还有其他捕获应用的方法</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>there are other ways to do the same thing. You can attach to Xcode by providing the process identifier, or PID, of a running program.
</code></pre></div></div>
<h3 id="attaching-to-a-future-process">Attaching to a future process</h3>
<p>如果程序没有运行,使用以上方法的话就是失败,下面介绍一种方法:LLDB 可以等待应用启动时捕获</p>
<p>使用 -w 参数,它会等待应用启动捕获</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-w, which causes LLDB to wait until a process launches with a PID or executable name matching the criteria supplied to the -w argument.
lldb -n Finder -w
</code></pre></div></div>
<p>将会在 Finder 下次启动的时候 attach,不信的话你可以新开一个终端会话窗口将 Finder 先杀掉</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pkill Finder
</code></pre></div></div>
<p>Finder 被杀掉后会重新启动,这时你会看到和之前一样的效果</p>
<p>另外一种方法就是直接 attach 可执行文件</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lldb -f /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder
</code></pre></div></div>
<p>准备好 debug 后,输入</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) process launch
Note: An interesting side effect is that stderr output (i.e. NSLog & company) are automatically sent to the Terminal window when manually launching a process. Other LLDB attaching configurations don’t do this automatically.
</code></pre></div></div>
<p>使用这种方式 attach,在终端自动输出 log,但是其他方式就不会自动输出</p>
<h3 id="options-while-launching">Options while launching</h3>
<p><strong><em>process launch</em></strong> 中有很多参数值得深究,如果你想更详细的列表,使用 <strong><em>help process launch</em></strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) help process launch
Launch the executable in the debugger.
Syntax: process launch <cmd-options> [<run-args>]
Command Options Usage:
process launch [-s][-A ] [-p <plugin>][-w ] [-a <arch>][-v ] [-c[<filename>]][-i ] [-o <filename>][-e ] [<run-args>]
...
</code></pre></div></div>
<p>新开一个终端,输入</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lldb -f /bin/ls
</code></pre></div></div>
<p>把 <strong><em>ls</em></strong> 当做可执行的目标文件
会得到这样的输出</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) target create "/bin/ls"
Current executable set to '/bin/ls' (x86_64).
</code></pre></div></div>
<p>无参数运行</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>process launch
</code></pre></div></div>
<p>输出</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) process launch
Process 40149 launched: '/bin/ls' (x86_64)
输出省略
Process 40149 exited with status = 0 (0x00000000)
</code></pre></div></div>
<p><strong><em>ls</em></strong> 将会在你进入的主目录启动,输出当前目录。可以使用 <strong><em>-w</em></strong> 更改当前工作目录</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) process launch -w /Applications
</code></pre></div></div>
<p>这个命令和下面的命令是等价的</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cd /Applications
$ ls
</code></pre></div></div>
<p>使用以下命令</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) process launch -- /Applications
</code></pre></div></div>
<p>和使用</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ls /Applications
</code></pre></div></div>
<p>效果是一样的</p>
<p>如果现在直接把工作目录直接切换到桌面会是怎样的</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) process launch -- ~/Desktop
Process 40286 launched: '/bin/ls' (x86_64)
ls: ~/Desktop: No such file or directory
Process 40286 exited with status = 1 (0x00000001)
</code></pre></div></div>
<p>这时候并不能有任何作用,这时候需要说明 <strong><em>~</em></strong> 的作用</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) process launch -X true -- ~/Desktop
</code></pre></div></div>
<p>这时会发现输出主目录的文件, <strong><em>-X</em></strong> 可以解释一些类似 <strong><em>~</em></strong> 的 <strong><em>shell</em></strong> 参数,在 <strong><em>LLDB</em></strong> 中有更简单的方法,使用 <strong><em>run</em></strong> 命令就可以了,想要自定义更多快捷命令请参考 <strong><em>Chapter 8, “Persisting and Customizing Commands”</em></strong></p>
<p>在 <strong><em>lldb</em></strong> 中输入</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) help run
Launch the executable in the debugger.
Syntax: run [<run-args>]
Command Options Usage:
run [<run-args>]
'run' is an abbreviation for 'process launch -X true --'
</code></pre></div></div>
<p>这时候使用 <strong><em>run</em></strong> 来一遍</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) run ~/Desktop
</code></pre></div></div>
<p>怎样把输出放到另一个地方呢?在第一节的时候使用 <strong><em>-e</em></strong> 将 <strong><em>stderr</em></strong> 输出到其他终端窗口</p>
<p>输入以下命令:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) process launch -o /tmp/ls_output.txt -- /Applications
</code></pre></div></div>
<p><strong><em>-o</em></strong> 输出到文件</p>
<p>输出为:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Process 40367 launched: '/bin/ls' (x86_64)
Process 40367 exited with status = 0 (0x00000000)
</code></pre></div></div>
<p>这时 <strong><em>ls</em></strong> 命令的输出是没有显示出来的</p>
<p>打开一个新的终端</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cat /tmp/ls_output.txt
</code></pre></div></div>
<p>会看到所有关于 /Applications 的所有输出</p>
<p>对于 <strong><em>stdin</em></strong> 的 <strong><em>-i</em></strong> 参数也是一样,输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) target delete
</code></pre></div></div>
<p>移除 target <strong><em>ls</em></strong>。然后,</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) target create /usr/bin/wc
Current executable set to '/usr/bin/wc' (x86_64).
</code></pre></div></div>
<p>设置一个新的 target, <strong><em>wc</em></strong>_ 可以统计输入的字符个数,文本行数
添加一些用于输入的文本,新开一个窗口</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>echo "hello world" > /tmp/wc_input.txt
</code></pre></div></div>
<p>将 hello world 写入 /tmp/wc_input.txt,作为 <strong><em>wc</em></strong> 输入</p>
<p>在 <strong><em>LLDB</em></strong> 中输入</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) process launch -i /tmp/wc_input.txt
Process 40412 launched: '/usr/bin/wc' (x86_64)
1 2 12
Process 40412 exited with status = 0 (0x00000000)
</code></pre></div></div>
<p>可以看到, 1 行, 2 单词, 12 字节
效果和以下方式相同:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ wc < /tmp/wc_input.txt
</code></pre></div></div>
<p>有时候你并不想要 <strong><em>stdin</em></strong>(standard input),这在一些类似 <strong><em>Xcode</em></strong> 的 <strong><em>GUI</em></strong>,但并不适用于类似 <strong><em>wc</em></strong> 和 <strong><em>ls</em></strong> 的终端命令</p>
<p>举个栗子,运行无参数的 <strong><em>wc</em></strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) run
Process 40460 launched: '/usr/bin/wc' (x86_64)
</code></pre></div></div>
<p>输入 <strong><em>hello world</em></strong>, 按 <strong><em>Enter</em></strong> 键,然后 <strong><em>Control + D</em></strong> 结束输入,这时你会看到标准的输出,像是这样</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) run
Process 40460 launched: '/usr/bin/wc' (x86_64)
hello world
1 2 12
Process 40460 exited with status = 0 (0x00000000)
</code></pre></div></div>
<p>这时,输入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>process launch -n
</code></pre></div></div>
<p>但 <strong>wc</strong> 回直接退出</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Process 40596 launched: '/usr/bin/wc' (x86_64)
Process 40596 exited with status = 0 (0x00000000)
</code></pre></div></div>
<p>使用 <strong>-n</strong> 参数不会有 <strong>stdin</strong> ;因此, <strong>wc</strong> 没有数据处理就直接退出了。</p>
<p>通过以上介绍,就可以使用 <strong>LLDB</strong> attach <strong>GUI</strong> 和 <strong>non-GUI</strong> 程序了。</p>klone1127以下内容整理自:<ADVANCED APPLE DEBUGGING & REVERSE ENGINEERING> 如有出入望指正Chapter 2- Help & Apropos2017-09-05T00:00:00+00:002017-09-05T00:00:00+00:00http://klone.space/%E6%8A%80%E6%9C%AF/2017/09/05/LLDB-Help<blockquote>
<blockquote>
<p>以下内容整理自:<a href="https://store.raywenderlich.com/products/advanced-apple-debugging-and-reverse-engineering"><ADVANCED APPLE DEBUGGING & REVERSE ENGINEERING></a> 如有出入望指正</p>
</blockquote>
</blockquote>
<blockquote>
<p>此文主要是讲怎么使用帮助文档,以及在记不清命令全称的时候进行模糊搜索</p>
</blockquote>
<h3 id="the-help-command">The “help” command</h3>
<p>在终端输入 lldb,进入 LLDB debugger 模式,键入:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) help
</code></pre></div></div>
<p>将会输出所有可用命令,包括在 ~/.lldbinit 自定义的命令</p>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/2017-09-05-LLDB-help?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="300px" /></p>
<p>这个help和man命令功能相似,查看 breakpoint 文档</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) help breakpoint
</code></pre></div></div>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/2017-09-05-LLDB-help-breakpoint?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="300px" /></p>
<p>如果你想查看 breakpoint 中 name 的用法</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) help breakpoint name
Commands to manage name tags for breakpoints
Syntax: breakpoint name <subcommand> [<command-options>]
The following subcommands are supported:
add -- Add a name to the breakpoints provided.
delete -- Delete a name from the breakpoints provided.
list -- List either the names for a breakpoint or the breakpoints for a
given name.
For more help on any particular subcommand, type 'help <command> <subcommand>'.
</code></pre></div></div>
<p>其他命令也可以使用这种方法进行查询</p>
<h3 id="the-apropos-command">The “apropos” command</h3>
<p>如果你只记得命令的一部分,可以使用 apropos 命令进行搜索, apropos 搜索的时候不区分大小写
假如你想搜索 swift 相关的东西</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) apropos swift
The following commands may relate to 'swift':
swift -- A set of commands for operating on the Swift Language Runtime.
demangle -- Demangle a Swift mangled name
refcount -- Inspect the reference count data for a Swift object
The following settings variables may relate to 'swift':
target.swift-framework-search-paths -- List of directories to be searched
when locating frameworks for Swift.
target.swift-module-search-paths -- List of directories to be searched when
locating modules for Swift.
target.use-all-compiler-flags -- Try to use compiler flags for all modules
when setting up the Swift expression parser,
not just the main executable.
</code></pre></div></div>
<p>apropos 还可以搜句子/词组,例如:你想搜和引用计数相关的东西</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) apropos "reference count"
The following commands may relate to 'reference count':
refcount -- Inspect the reference count data for a Swift object
</code></pre></div></div>
<blockquote>
<p>提示</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Notice the quotes surrounding the words "reference count". apropos will only accept one argument to search for, so the quotes are necessary to treat the input as a single argument.
</code></pre></div></div>
<p>本节简要说了 help 和 apropos 两个命令,因为众多命令不一定可以全部记住,这时候这两个命令就用处大了。</p>klone1127以下内容整理自:<ADVANCED APPLE DEBUGGING & REVERSE ENGINEERING> 如有出入望指正Chapter 1- Getting Started2017-09-04T00:00:00+00:002017-09-04T00:00:00+00:00http://klone.space/%E6%8A%80%E6%9C%AF/2017/09/04/LLDB-GettingStarted<blockquote>
<blockquote>
<p>以下内容整理自:<a href="https://store.raywenderlich.com/products/advanced-apple-debugging-and-reverse-engineering"><ADVANCED APPLE DEBUGGING & REVERSE ENGINEERING></a> 如有出入望指正</p>
</blockquote>
</blockquote>
<blockquote>
<p>由于OSX EI Capitan(10.11)开始引入了<a href="http://devstreaming.apple.com/videos/wwdc/2015/706nu20qkag/706/706_security_and_your_apps.pdf"><strong><em>SIP(System Integrity Protection/系统完整性保护机制)</em></strong></a>, 所以首先关闭SIP</p>
</blockquote>
<ul>
<li>重启电脑</li>
<li>开机按 Command + R,此时进入Recovery 模式</li>
<li>点击Utilities(工具),选择 Terminal,并输入</li>
</ul>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code> csrutil disable<span class="p">;</span> reboot
</code></pre></div></div>
<p>关闭SIP,
要开启SIP,使用</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code> csrutil endable<span class="p">;</span> reboot
</code></pre></div></div>
<p>Terminal 中输入 lldb,</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code> lldb <span class="nt">-n</span> Finder
</code></pre></div></div>
<p>输出类似:</p>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/2017-09-04-lldb_-n_Finder?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="300px" /></p>
<h2 id="attaching-lldb-to-xcode">Attaching LLDB to Xcode</h2>
<blockquote>
<p>使用 LLDB 调试 Xcode</p>
</blockquote>
<p>打开一个新的终端,然后, ⌘ + Shift + I ,更改标签页标题为 LLDB</p>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/2017-09-04-LLDB-Window?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="300px" /></p>
<p>确保没有 Xcode 正在运行,以防运行多个混淆
使用 ⌘ + T 新开一个, ⌘ + Shift + I , 命名为Xcode stderr</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~$ tty
</code></pre></div></div>
<p>获取到当前窗口为</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/dev/ttys003
</code></pre></div></div>
<p>这个可能会不同
新开一个窗口,输入</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>echo "hello debugger" 1>/dev/ttys003
</code></pre></div></div>
<p>这时在 <strong><em>Xcode stderr</em></strong> 中可以看到 hello debugger 打印出来了,关闭最后打开的窗口</p>
<p>切换到 LLDB 窗口,输入 lldb</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) file /Users/mylove/Downloads/Xcode-beta.app/Contents/MacOS/Xcode
</code></pre></div></div>
<p>我使用的是Xcode 9 beta 版,正式版可能是</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) file /Applications/Xcode.app/Contents/MacOS/Xcode
</code></pre></div></div>
<p>也可以使用</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ps -ef `pgrep -x Xcode` 查找
</code></pre></div></div>
<p>然后在LLDB 窗口输入</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) process launch -e /dev/ttys003 --
</code></pre></div></div>
<p>这时你可以在 Xcode stderr 看到Xcode输出的log,Xcode 也会被打开</p>
<blockquote>
<p>新建swift项目</p>
</blockquote>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/2017-09-04-new-project?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="300px" /></p>
<blockquote>
<p>切换到Xcode,打开ViewController.swift</p>
<h3 id="finding-a-class-with-a-click">Finding a class with a click</h3>
</blockquote>
<p>-[NSView hitTest:] 在点击view的时候会被触发,我们通过给此方法添加断点进行调试
切换到 LLDB 窗口,Ctrl + C 暂停 debugger</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) breakpoint set -n "-[NSView hitTest:]"
Breakpoint 2: where = AppKit`-[NSView hitTest:], address = 0x00007fffcb22b92b
</code></pre></div></div>
<p>此时处于debugger状态,输入continue</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) continue
Process 13166 resuming
</code></pre></div></div>
<p>点击Xcode,任意位置</p>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/2017-09-04-LLDB-debuggerXcode-continue?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="300px" /></p>
<p>使用 RDI 进行输出</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) po $rdi
<_NSThemeCloseWidget: 0x128a1e9b0>
</code></pre></div></div>
<p>hitTest: 方法被触发,点击的位置不同,得到的结果或许不同</p>
<h3 id="filter-breakpoints-for-important-content">Filter breakpoints for important content</h3>
<p>给断点添加过滤条件,只有属于 NSTextView 类才会被触发</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) breakpoint modify 2 -c "(BOOL)[$rdi isKindOfClass:[NSTextView class]]"
</code></pre></div></div>
<p>其中,2是要修改的断点,可以通过以下方法查看</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) breakpoint list
Current breakpoints:
2: name = '-[NSView hitTest:]', locations = 1, resolved = 1, hit count = 1
2.1: where = AppKit`-[NSView hitTest:], address = 0x00007fffcb22b92b, resolved, hit count = 1
</code></pre></div></div>
<p>因为我之前删了一个断点,所以现在的是2
之后就是点击Xcode,如果是 NSTextView 或者是其子类,都会响应。书中说是点击 code area in Xcode (代码区域),断点会停下来,但是我点击后也没有停下来(Xcode 9 deta 6),我是点击target version 区域</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) po $rdi
<NSTextView: 0x13d18d960>
Frame = {\{2.00, 3.00}, {266.00, 14.00}\}, Bounds = {\{0.00, 0.00}, {266.00, 14.00}\}
Horizontally resizable: YES, Vertically resizable: YES
MinSize = {266.00, 14.00}, MaxSize = {40000.00, 40000.00}
</code></pre></div></div>
<p>输出值
(lldb) po [$rdi string]
1.0</p>
<p>以上都是使用 Objective-C, 下面来确认下在 swift 中也可以使用,导入Foundation,AppKit</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) ex -l swift -- import Foundation
(lldb) ex -l swift -- import AppKit
</code></pre></div></div>
<p>ex 是 expression 的缩写,-l swift 告诉 LLDB 这是 swift 代码,通过替换之前的内存地址</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) ex -l swift -o -- unsafeBitCast(0x13d18d960, to: NSObject.self)
(lldb) ex -l swift -o -- unsafeBitCast(0x13d18d960, to: NSObject.self) is NSTextView
</code></pre></div></div>
<p><strong><em>得到的结果和文中的有出入,稍后我会研究一下</em></strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) ex -l swift -o -- unsafeBitCast(0x13d18d960, to: NSObject.self)
error: Couldn't lookup symbols:
__TTSfq4n_n_d_d_n___TFs18_fatalErrorMessageFTVs12StaticStringS_4fileS_4lineSu5flagsVs6UInt32_Os5Never
(lldb) ex -l swift -o -- unsafeBitCast(0x13d18d960, to: NSObject.self) is NSText
error: Couldn't lookup symbols:
__TTSfq4n_n_d_d_n___TFs18_fatalErrorMessageFTVs12StaticStringS_4fileS_4lineSu5flagsVs6UInt32_Os5Never
</code></pre></div></div>
<p>文中正确的结果,类似这种:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) ex -l swift -o -- unsafeBitCast(0x14bdd9b50, NSObject.self) <NSTextViewSubclass: 0x14b7a65c0>
Frame = {\{0.00, 0.00}, {1089.00, 1729.00}}, Bounds = {\{0.00, 0.00}, {1089.00, 1729.00}}
Horizontally resizable: NO, Vertically resizable: YES
MinSize = {1089.00, 259.00}, MaxSize = {10000000.00, 10000000.00}
(lldb) ex -l swift -o -- unsafeBitCast(0x14bdd9b50, NSObject.self) is NSTextView
true
</code></pre></div></div>
<p>使用 Swift 需要很多必要的条件,所以LLDB默认使用的是 Objective-C</p>
<p>更改version值</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>po [$rdi setString:@"1.1"]
po [CATransaction flush]
</code></pre></div></div>
<p>观察Xcode 中version 值得变化,此时变为1.1</p>
<h3 id="hunting-for-private-classes-and-methods-in-modules">Hunting for private classes and methods in modules</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) image lookup -rn 'NSTextView\ '
</code></pre></div></div>
<p>从正在运行的二进制和加载的动态库中查找 NSTextView 相关class</p>
<blockquote>
<p>原文介绍</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>This command lets you introspect the running binary and all loaded dynamic libraries. The r option instructs it to use a regular expression search. The n option instructs it to search functions or symbols by name.
You’ll see a list of methods your NSTextView subclass implements!
</code></pre></div></div>
<h3 id="swizzling-with-block-injection">Swizzling with block injection</h3>
<p>通过 runtime 机制进行方法替换</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) po @import Foundation
</code></pre></div></div>
<p>尽管Xcode中已经包含了这个库,但是 LLDB 并不知道识别不了,导入是为了在使用LLDB调试runtime 时使用</p>
<blockquote>
<p>现在输入</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) po
Enter expressions, then terminate with an empty line to evaluate:
1:
</code></pre></div></div>
<p>输入以下代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@import Cocoa;
id $class = [NSObject class];
SEL $sel = @selector(init);
void *$method = (void *)class_getInstanceMethod($class, $sel);
IMP $oldImp = (IMP)method_getImplementation($method);
</code></pre></div></div>
<p>输入完就 enter(回车)</p>
<p>在 LLDB 中,所有变量都是使用 $ 作为开头</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) po $class
NSObject
(lldb) po $oldImp
(libobjc.A.dylib`-[NSObject init])
</code></pre></div></div>
<p>使用 imp_implementationWithBlock 新建一个 IMP,使用 po 输入</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>id (^$block)(id) = ^id(id object) {
if ((BOOL)[object isKindOfClass:[NSView class]]) {
fprintf(stderr, "%s\n", (char *)[[[object class] description] UTF8String]);
}
return object;
};
IMP $newImp = (IMP)imp_implementationWithBlock($block);
method_setImplementation($method, $newImp);
</code></pre></div></div>
<p>此方法的目的是 swizzle -[NSObject init], -[NSObject init]除了返回它自己,什么也不做,新建的block检测object是否属于 NSView 类,是的话就输出出来</p>
<p>文中解释其如何工作:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1. You create a block that takes an object reference.
2. The block checks if the object passed in is of type NSView.
3. If so, it prints a description of the view to stderr, which will appear on your Xcode stderr Terminal tab.
4. It then returns object, to perform the equivalent implementation of -[NSObject init] that it’s swizzling. Ideally, swizzling these implementations would have been cleaner, and you could simply execute $oldImp with the right parameters. However, there is a bug in LLDB that will crash when executing IMPs inside of a block.
5. Finally, a new IMP is created from the block, and the method implementation is set to this new IMP. This has therefore swizzled -[NSObject init] with your new implementation.
</code></pre></div></div>
<p>使用 continue 开始debugging。
在Xcode上点击,在Xcode stderr窗口中将会看到符合要求的输出</p>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/2017-09-04-LLDB-blockSwizzleInit-View?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="300px" /></p>
<p>第一节就先这样,有不足之处再进行补充。</p>klone1127以下内容整理自:<ADVANCED APPLE DEBUGGING & REVERSE ENGINEERING> 如有出入望指正Malloc Stack 开启后打印大量 log 问题2017-04-17T00:00:00+00:002017-04-17T00:00:00+00:00http://klone.space/%E6%8A%80%E6%9C%AF/2017/04/17/Malloc-Stack-log<blockquote>
<p>最近使用手机调试公司老项目的时候,测一次发现手机内存不够一次,腾出2G一会儿就用完了,然后发现那个缓存往上飙的老厉害了。正式版上没看到这个问题,然后……就被排期到了今天。</p>
</blockquote>
<p><strong><em>因为项目中集成了一些第三方SDK,使用模拟器报错,还得把相关代码注释了才能运行,比较麻烦……只能使用真机解决这个问题。</em></strong></p>
<ul>
<li>1 、使用 Instruments 分析,但分析了一番后也没发现哪里不对劲。</li>
<li>2、脱离 Xcode 测试,想到正式版并没有这个问题,然后就弄了个开发版装手机上运行测试,缓存显示一直没有什么大的变化</li>
</ul>
<blockquote>
<p>这么看来问题只有在连上 Xcode 测试的时候才会出现,那就是 Xcode 的配置问题了吧,</p>
</blockquote>
<ul>
<li>3、 使用 Xcode 运行,查看了 Xcode 性能监控提示发现…磁盘显示…惊人</li>
</ul>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/Xcode-%E6%80%A7%E8%83%BD%E6%98%BE%E7%A4%BA.png?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="300px" /></p>
<p>点进去看了发现:</p>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/Malloc-Stack-logs-File.png?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="300px" /></p>
<p>问题就是出在这儿了,一直有 log 在写入,那这个文件属于谁呢,在debug时有时需要打开 zombie 调试和 logging 调试,以帮助我们定位 bug,好了,到此已经算是解决问题了</p>
<blockquote>
<h4 id="edit-scheme---run---diagnostics---去掉打钩-malloc-stack">Edit Scheme -> Run -> Diagnostics -> 去掉打钩 Malloc Stack</h4>
</blockquote>
<p>去掉之后就会解决这个问题了,其实在控制器也会提示打印这个 log 。</p>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/noselect-Malloc-Stack.png?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="300px" /></p>klone1127最近使用手机调试公司老项目的时候,测一次发现手机内存不够一次,腾出2G一会儿就用完了,然后发现那个缓存往上飙的老厉害了。正式版上没看到这个问题,然后……就被排期到了今天。自定义 Xcode Warning2017-04-13T00:00:00+00:002017-04-13T00:00:00+00:00http://klone.space/%E6%8A%80%E6%9C%AF/2017/04/13/Custom-Xcode-Warning<blockquote>
<p>在项目中总免不了想要添加一些类似 TODO/FIXME 之类的提示信息,但 Xcode 自带的 warning 提示有时用着并不是那么爽, 还不识别中文(不要问我为啥不用英文写… :joy:)</p>
</blockquote>
<blockquote>
<h4 id="下面介绍一种通过-shell-脚本添加自定义-warning-的方式其他方式还没怎么去探索">下面介绍一种通过 shell 脚本添加自定义 warning 的方式,其他方式还没怎么去探索</h4>
</blockquote>
<ul>
<li>
<p>点击 target -> Build Phases -> 添加 New Run Script Phases</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">TAGS</span><span class="o">=</span><span class="s2">"TODO:|FIXME:"</span>
<span class="nb">echo</span> <span class="s2">"searching </span><span class="k">${</span><span class="nv">SRCROOT</span><span class="k">}</span><span class="s2"> for </span><span class="k">${</span><span class="nv">TAGS</span><span class="k">}</span><span class="s2">"</span>
find <span class="s2">"</span><span class="k">${</span><span class="nv">SRCROOT</span><span class="k">}</span><span class="s2">"</span> <span class="se">\(</span> <span class="nt">-name</span> <span class="s2">"*.h"</span> <span class="nt">-or</span> <span class="nt">-name</span> <span class="s2">"*.m"</span> <span class="se">\)</span> <span class="nt">-print0</span> | xargs <span class="nt">-0</span> egrep <span class="nt">--with-filename</span> <span class="nt">--line-number</span> <span class="nt">--only-matching</span> <span class="s2">"(</span><span class="nv">$TAGS</span><span class="s2">).*</span><span class="se">\$</span><span class="s2">"</span> | perl <span class="nt">-p</span> <span class="nt">-e</span> <span class="s2">"s/(</span><span class="nv">$TAGS</span><span class="s2">)/ warning: </span><span class="se">\$</span><span class="s2">1/"</span>
</code></pre></div> </div>
</li>
</ul>
<blockquote>
<p>在项目中添加 // TODO: 编译后就可以看到了。</p>
</blockquote>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/Custom-Xcode-Warning.png?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="300px" /></p>
<blockquote>
<p>参考:</p>
</blockquote>
<blockquote>
<blockquote>
<p><a href="http://jeffreysambells.com/2013/01/31/generate-xcode-warnings-from-todo-comments">Jeffrey Sambells-Generate Xcode Warnings from TODO Comments</a></p>
</blockquote>
</blockquote>klone1127在项目中总免不了想要添加一些类似 TODO/FIXME 之类的提示信息,但 Xcode 自带的 warning 提示有时用着并不是那么爽, 还不识别中文(不要问我为啥不用英文写… :joy:)react-native 安卓编译出错2017-02-20T00:00:00+00:002017-02-20T00:00:00+00:00http://klone.space/%E6%8A%80%E6%9C%AF/2017/02/20/React-Native%E5%AE%89%E5%8D%93%E7%BC%96%E8%AF%91%E5%87%BA%E9%94%99<h3 id="react-native-安卓编译出错">react-native 安卓编译出错</h3>
<p>新建了一个 RN 项目,使用 Android Studio 导入打开项目的时候报错:</p>
<blockquote>
<p>Error:(24, 0) Could not find method android() for arguments [build_bvzjv4mjldob73mp1agwx72x5$_run_closure2@7a053e6a] on root project ‘TestHelloWorld’ of type org.gradle.api.Project.
<a href="openFile:/Users/mylove/TestHelloWorld/android/build.gradle">Open File</a></p>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/2017-02-20-React-Native%E5%AE%89%E5%8D%93%E7%BC%96%E8%AF%91%E5%87%BA%E9%94%99-Gradle_Error.png?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="300px" /></p>
</blockquote>
<p>首先我是根据提示信息 Try Again , ———— 无用</p>
<p>看 log 信息也没得出个所以然来</p>
<p>点了 Open File 后直接定位到了Gradle Script/build.gradle 下的</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>android {
compileSdkVersion 23
buildToolsVersion '23.0.2'
}
dependencies {
}
</code></pre></div></div>
<p>,虽然是直接定位到了这里,但并不知道解决办法,在网上搜了一番,试了多个方法后也是无效的</p>
<p>最后,无奈删除了这两段代码,然后编译 -> 运行,嗯,可以了,至于原因,我还是没想清楚为啥,稍后对这块了解的多了再回来解释这个问题!</p>klone1127react-native 安卓编译出错UI/WKWebView 的 POST 请求2016-03-13T00:00:00+00:002016-03-13T00:00:00+00:00http://klone.space/ios/2016/03/13/UI:WKWebView-POST-Method<blockquote>
<p>首先说下我遇到的问题:在项目中加载的是 HTML5 ,用户退出时发送的请求是 DELETE 请求,服务器使用的是 Ruby ,Ruby 本身并不能实现 DELETE 方法(<a href="http://railscasts.com/episodes/77-destroy-without-javascript">具体看这里</a>),大概来说它就是 POST 方法,所以我在 UI/WKWebView 中使用 POST 方法就可以了,其实 UI/WKWebView 默认 HTTP 请求是 GET 请求,所以这里可以改用 POST 方法。</p>
</blockquote>
<p>代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+ (void)HTTPPOSTMethodWithRequest:(NSURLRequest *)request URL:(NSString *)urlString blockWithReq:(requestBlock)requestBlock {
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
if ([request.HTTPMethod isEqualToString:@"POST"]) {
req.HTTPBody = request.HTTPBody;
req.HTTPMethod = request.HTTPMethod;
}
requestBlock(req);
}
</code></pre></div></div>
<p>核心代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
if ([request.HTTPMethod isEqualToString:@"POST"]) {
req.HTTPBody = request.HTTPBody;
req.HTTPMethod = request.HTTPMethod;
}
</code></pre></div></div>
<p>这样加载的页面发送的就是 POST 请求了,我的问题就是这样解决的。</p>klone1127首先说下我遇到的问题:在项目中加载的是 HTML5 ,用户退出时发送的请求是 DELETE 请求,服务器使用的是 Ruby ,Ruby 本身并不能实现 DELETE 方法(具体看这里),大概来说它就是 POST 方法,所以我在 UI/WKWebView 中使用 POST 方法就可以了,其实 UI/WKWebView 默认 HTTP 请求是 GET 请求,所以这里可以改用 POST 方法。Reveal 破解及简单使用2016-02-18T00:00:00+00:002016-02-18T00:00:00+00:00http://klone.space/%E8%AE%BE%E8%AE%A1/2016/02/18/Reveal<blockquote>
<p><a href="http://7xq3rg.com1.z0.glb.clouddn.com/Blog-Reveal%10">Reveal 1.6.2 及破解方法下载</a> 下载后将文件改为 zip/rar 后缀名,解压即可。</p>
</blockquote>
<blockquote>
<p>Reveal 是一个界面调试分析的工具,用它你可以对正在测试的应用界面进行调试,越狱苹果可调试任意程序!</p>
</blockquote>
<h3 id="1使用静态方法添加">1、使用静态方法添加</h3>
<p>打开Reveal -> help</p>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/Reveal-help_GUI?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="300px" /></p>
<p>调试 iOS 打开上面的 iOS,将 <strong><em>Reveal.framework</em></strong> 拖入工程,然后从 build-phases 中移除
<img src="http://olnx7jkmx.bkt.clouddn.com/Remove-RevealFramework-From-Libraries?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="300px" />
最后一步:<strong><em>-ObjC -zl -framework Reveal</em></strong> 添加到 other linker flags</p>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/Add-to-other-linker-flags?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="300px" />
现在运行模拟器就可以使用了。</p>
<p><img src="http://olnx7jkmx.bkt.clouddn.com/Run-Reveal?imageView2/0/interlace/1/q/100|watermark/2/text/a2xvbmUuc3BhY2U=/font/5b6u6L2v6ZuF6buR/fontsize/500/fill/I0YzRjBGMA==/dissolve/86/gravity/SouthEast/dx/10/dy/10" width="300px" /></p>
<p>还有其他几种方法,使用 cocoapods 和动态加载之类的</p>
<p>另附:</p>
<p><a href="http://support.revealapp.com/kb/getting-started/reveal">集成教程(官方中文版)</a></p>
<p><a href="http://revealapp.com/blog/reveal-common-tips-cn.html">Reveal 常用技巧</a></p>
<p><a href="http://c.blog.sina.com.cn/profile.php?blogid=cb8a22ea89000gtw">Reveal查看任意app的高级技巧</a></p>
<p></p>klone1127Reveal 1.6.2 及破解方法下载 下载后将文件改为 zip/rar 后缀名,解压即可。