原文:How To Create an Xcode Plugin: Part 2/3html
原做者:Derek Selanderios
譯者:@yohunl正則表達式
譯者注:原文使用的是xcode6.3.2,我翻譯的時候,使用的是xcode7.2.1,通過驗證,本部分中說的依然是有效的.在文中你能夠學習到一系列的技能,很是值得一看.這些技能不僅僅只是用來建立插件,對你平時的調試等,也有很是大的幫助.macos
歡迎你來到建立xcode插件教程的第二部分.在第一部分中, 你已經瞭解了怎麼經過NSNotification來窺探xcode的內部類,而且代碼注入了其私有類DVTBezelAlertPanel,而且,你還 添加了一個NSMenuItem菜單到xcode的菜單欄裏,經過它來持久化是否開啓咱們自定義的插件Rayrolling的功能api
在這個教程中,你會在第一部分中建立的Rayrolling的基礎上繼續向前,若是你並無在這以前閱讀第一部分或者你想從本處開始,你能夠下載第一部分完成的demo.本章中,你將會經過深刻了解一些可用的工具來進一步探索Xcode,利用你將會學到的新知識,來修改Xcode的標題欄,使標題欄改成展現Ray的某首歌的歌詞。xcode
開始啦app
打 開Xcode和Terminal(終端),而且將它倆都平鋪在桌面上,這樣你能夠同時看到它們,方便後續的操做.(yohunl注:後文將會一面在 Xcode上點擊,一面觀察終端控制檯的輸出,一旦發現咱們須要的輸出,就要立馬中止終端控制檯,因此,平鋪在桌面上才便於你操做,不然,你就忙不過來 啦)dom
因 爲從上一章,你已經學會了從xcode運行一個xcode實例來建立插件和調試插件,因此咱們換一種新的方式:你將會直接經過終端命令來使用LLDB來探 索Xcode,你不用再像第一部分那樣從一個Xcode啓動另外一個調試用的Xcode實例來探索Xcode是怎樣工做的了.ide
LLDB的BFF和DTrace函數
DTrace是最好的探索Xcode的工具之一,它是一款至關優秀的調試工具,它是Instruments的基石.只要你瞭解怎麼使用它,你將會發現它是很是有用的工具.
首 先,爲了對其有個瞭解,咱們須要一個使用DTrace的demo,就如同咱們學習一門新語言時候,老是會經過一個Hello Worlddemo同樣.那麼開始吧… 建立一個腳本,它是用來保持全部以IDE開頭的類的運行計數,並在每次你調用該特定類方法或者實例方法時,增長計數.在你退出腳本時候,DTrace將會 轉儲這些數據.
運行Xcode,而後,在Terminal的一個新窗口中,輸入以下的腳本:
1
|
sudo dtrace -n
'objc$target:IDE*::entry { @[probemod] = count(); }'
-p `pgrep -xo Xcode`
|
雖 然你鍵入命令後並不會看到任何的輸出,可是DTrace已經在後臺默默的生成了全部方法調用的蹤影(trace).回到Xcode,隨便嘗試一些操做.打 開某些文件,隨便作一些點擊操做.回到Terminal,而後按Ctrl+C來結束腳本,腳本產生的內容將會輸出到Terminal上 (只有在你Ctrl + C終止腳本後,才能在Terminal上看到腳本輸出的內容)
至關酷吧:),其實使用Dtrace,你能夠作到不少事,可是咱們的教程不會覆蓋你想了解的DTrace的所有,相反的,咱們提供一個快速的Dtrace語法的概述,它將會幫助你開始:
1.Probe Description(探頭說明,前序描述):由提供者(Provider),模塊(Module),功能(Function),名字(Name)組 成,它們由冒號分隔.省略它們中的任何一個,將會使Probe Description包含全部的匹配項.你可使用 * 和 ? 操做符來作模式匹配.
Provider:這個包含了一系列的類和方法,例如profile,fbt,或者io.在咱們的教程中,主要使用objc來hookObjective-C的方法調用.
Module:在OC語言中,這部分指示的是你但願觀察的指定的類名
Function:指示你但願觀察的特定方法名
Name:雖然根據你的Provider的不一樣,有不一樣的name值可選,可是你在此只須要使用entry或者retrun,來匹配函數的開始或者結束.
你要了解,你可使用$target關鍵字來匹配進程ID,也可使用p或者c標誌來指定target.
2.Predicate(謂詞部分): 可選的表達式,它是用來過濾哪些動做是知足條件的.你能夠把它當作一個if語句的條件部分.
3.Action(動做): 將要執行的操做.它能夠只是簡單的輸出一些內容到控制檯,也能夠是執行一個高級功能.
就如同LLDB的命令image lookup -rn {Regex Query}同樣,你也可使用l標誌來轉儲(dump)特定進程中的類和方法.
爲了演示這點,咱們來看這樣一個簡單的例子,運行Safari,而後在終端輸入如下腳本
1
|
sudo dtrace -ln
'objc$target::-*ecret*:entry'
-p `pgrep -xn Safari`
|
上述的DTrace腳本會打印出全部的名字中包含ecret字符串的實例方法.由於Probe Description部分 提供了Name參數entry,全部的方法都有entry和return,因此基本上忽略了全部的重複查詢請求.
注意:若是你想掌握更多的Dtarce知識,你能夠參考Big Nerd的文章和 這本Dtrace書籍.若是並無理解全部的這篇快速介紹中介紹的內容,也不須要沮喪,由於Dtrace是一個至關複雜的工具.
現 在你瞭解到Dtace的最基本的內容,咱們能夠開始使用它來尋找咱們感興趣的NSViews了.由於Xcode含有大量的Views,你會發現,使用 LLDB來試圖找出它們,讓你不堪重負.即便結合LLDB的條件斷點,調試一些應用程序都包含的共同東西,也是一個醜陋的過程.
幸運的是,使用Dtrace,將能帶給你對付此類問題的巨大的幫助.你將會使用DTrace來找出Xcode中組成標題欄的視圖(NSViews).(⊙o⊙)…….那麼該怎麼作呢?
這是一種方法:當鼠標中止在,或者點擊一個NSView,hitTest:方法將會被觸發,這個方法將會返回在這個鼠標點下最深的子視圖.你可使用DTrace來順着hitTest:方法來肯定視圖,那個你應該進一步探索其其父視圖和子視圖的視圖.
在終端中輸入如下腳本:
1
|
sudo dtrace -qn
'objc$target:NSView:-hitTest?:return /arg1 != 0/ { printf("UIView: 0x%x\n", arg1); }'
-p `pgrep -xo Xcode`
|
一 旦這段腳本運行,請肯定你的Xcode是第一響應者(經過在Xcode上任何地方點擊一下便可成爲第一響應者).在Xcode的窗口上移動你的鼠標指針, 當你中止移動鼠標指針的時候,Dtrace將會輸出一個內存地址屢次,這是由於 hitTest: 被在視圖層次上的多個視圖調用.
定位到Xcode的標題欄,點擊標題欄.選擇出如今終端中最近的地址,拷貝到你的粘貼板中
打開一個新的終端窗口標籤,運行LLDB,並將其附着到Xcode上,而後輸出從DTrace中拷貝出的地址,像如下這樣操做:
1
2
3
4
5
|
> lldb
(lldb) pro attach -n Xcode
...
(lldb) po {上一步拷貝到粘貼板中的對象地址,例如是0x7fcc2687cd40,則該命令是 po 0x7fcc2687cd40}
...
|
其 中,pro 是 progress的縮寫,LLDB使用的是前序匹配原則,在不引發歧義的狀況下 process,proc,proce都是能夠的.在下面你還會看到 pro at -n Xcode這種寫法,都是由於這個原則,都是等效的. pro attach -n Xcode ,使用attach命令直接在LLDB中把一個正在運行的進程鏈接到LLDB中,以便於進行動態調試.也就是說這個命令直接能夠將LLDB添加到任意的其 它不是由它本身建立的進程中!!
你將會看到和如下類似的輸出:
注 意:如今,Xcode被咱們經過終端輸入的LLDB命令所暫停了.你能夠在任什麼時候候經過輸入process interrupt或者其縮寫pro i來暫停Xcode,從而可以開始調試Xcode.另外,你也能夠經過在任什麼時候候輸入continue或者縮寫c來恢復Xcode的運行.請肯定你老是了 解附着於Xcode上的LLDB的狀態,你能夠認爲,當Xcode被你的調試LLDB所暫停時候,它是不可以相應你的單擊等操做的(如同你在Xcode中 打了斷點,調試的時候進入到斷點同樣)
取決於你在Xcode中點擊的地方,你可能會命中一系列的視圖.探索這些內存地址的父視圖(經過在lldb中 po [對象地址 supview])或者子視圖(po [對象地址 subvies]),直到你獲得了視圖 IDEActivityView.
一旦你找到了IDEActivityView,怎樣肯定這個視圖就是你所但願找尋的視圖呢?
經過在LLDB中輸入如下命令:
1
2
3
4
|
(lldb) po [{找到的IDEActivityView實例的地址} setHidden:YES] 在個人機子上是 po [0x7fcc2686b3c0 setHidden:YES]
...這裏的點表示咱們省略了lldb的某些不重要的輸出.
(lldb) c
...
|
若是是咱們所指望的那個標題視圖,那麼如今Xcode的標題欄就被隱藏了,這就能證明它的確是咱們所指望的標題欄的視圖啦.
經過以下命令來從新顯示它:
1
2
3
|
(lldb) pro i
(lldb) po [{找到的IDEActivityView實例的地址} setHidden:NO] 好比在個人機子上是 po [0x7fcc2686b3c0 setHidden:NO]
(lldb) c
|
如下這個圖是上述在LLDB中輸入命令的gif動畫,供你參考:
按 照以往的經驗,你知道,當你Build或者Stop一個Xcode工程的時候,標題欄視圖的內容將會變化.你能夠用DTrace來觀察相關的函 數.IDEActivity前綴是一個獨特的命名約定,你能夠觀察全部的以IDEActivity開頭的類,以此來觀察在這幕後的全部相聯繫的事情.
回到終端,經過Control + C來中止DTrace,而後輸入如下命令到終端中:
1
|
sudo dtrace -qn
'objc$target:IDEActivity*::entry { printf("%c[%s %s]\n", probefunc[0], probemod, (string)&probefunc[1]); @num[probemod] = count(); }'
-p `pgrep -xn Xcode`
|
這段腳本將會輸出全部類名前綴是IDEActivity的全部方法調用.一旦你中止這個腳本命令(Control+C),它還會輸出指定類方法的被調用次數.
yohunl備註: 當你輸入這個命令的時候,可能會遇到譯者遇到的這種狀況
yohunldeMacBook-Pro:~ yohunl$ sudo dtrace -qn 'objc$target:IDEActivity*::entry { printf("%c[%s %s]\n", probefunc[0], probemod, (string)&probefunc[1]); @num[probemod] = count(); }' -p `pgrep -xn Xcode`
dtrace: invalid probe specifier objc$target:IDEActivity*::entry { printf("%c[%s %s]\n", probefunc[0], probemod, (string)&probefunc[1]); @num[probemod] = count(); }: probe description objc50360:IDEActivity*::entry does not match any probes
當遇到這種狀況不要擔憂,不要茫然,你只要徹底退出Xcode,再從新啓動Xcode,再輸入這個命令就行了.
開始這個DTrace命令,回到Xcode中,隨便打開一個工程,編譯並運行,而後再中止.注意觀察Xcode的標題欄的內容變化,而後中止Dtrace命令,查看結果:
仔細分析上面輸出的信息.IDEActivityView和其它類之間是怎麼運轉的都在控制檯的輸出信息中,可是也有大量的其它信息夾雜在其中(控制檯的輸出信息中),不是麼?
幸 運的是,你能夠有選擇的限制展現給你的信息量.經過瀏覽相關的類,來肯定是否有值得你進一步選擇性探索的內容….,也許 IDEActivityReport* 就是一個不錯的候選類,由於以它開頭的類看起來就是有聯繫的(在咱們上述的操做中,最後在DTrace看到的不少都是它,因此咱們有理由以爲它就是有聯繫 的).
修改Dtrace腳本,使他看起來以下:
1
|
sudo dtrace -qn
'objc$target:IDEActivityReport*::entry { printf("%c[%s %s]\n", probefunc[0], probemod, (string)&probefunc[1]); @num[probemod] = count(); }'
-p `pgrep -xn Xcode`
|
再運行,中止Xcode的動做的同時,密切關注控制檯的輸出信息.一旦中止Xcode,立馬Control+C中止掉Dtrace的腳本,是否出現了一些類,看起來是值得咱們進一步去探索的呢?
IDEActivityReportStringSegment 看起來比較值得探索.縮小咱們的腳本的搜索範圍,聚焦到這個類上來,注意如下命令的變化,注意Probe的Function部分的變化(咱們上面分析了 Dtrace命令的組成,其中Probe部分有個Function組成部分)
1
|
sudo dtrace -qn
'objc$target:IDEActivityReportStringSegment::entry { printf("%c[%s %s]\n", probefunc[0], probemod, (string)&probefunc[1]); @num[probefunc] = count(); }'
-p `pgrep -xn Xcode`
|
通 過編譯,運行,中止Xcode工程;再一次中止Dtrace命令,觀察IDEActivityReportStringSegment類的實例的方法各自 被調用的次數(當Control+C中止命令後,在控制檯會輸出各個方法的調用次數).看起 來,initWithString:priority:frontSeparator:backSeparator: 和 initWithString:priority:值得進一步分析!
打開一個新的LLDB會話(打開一個終端標籤頁面,lldb進入LLDB),而後再運行以下命令:
1
2
3
4
5
6
7
8
9
|
(lldb) pro attach -n Xcode
(lldb) rb
'IDEActivityReportStringSegment\ initWithString'
Breakpoint 9: 2 locations.
(lldb) br command add
Enter your debugger command(s). Type
'DONE'
to end.
> po NSLog(@
"customidentifier %s %@"
, $rsi, $rdx)
> c
> DONE
(lldb) c
|
在 上面這段命令中,你添加了自定義的命令,當任何屬於IDEActivityReportStringSegment類的以initWithString方 法開頭的方法被調用時,就會執行添加的自定義命令.它輸出IDEActivityReportStringSegment實例的方法的 Selector 和 self (還記得教程一中提到的各個寄存器中存放的內容了吧)到控制檯.
另外,使用了customidentifier 來標識 NSLog的輸出信息,這樣你能夠更容易的從控制檯的輸出中容易的定位到這些Log.
回到終端,新建一個終端tab(使用快捷鍵 command + t ),創建一個customidentifier的grep尾搜索:
1
|
tail -f /
var
/log/system.log | grep customidentifier
|
譯者注,這句話輸入後,並無輸出,當你切換到Xcode中,編譯,運行,就產生輸出了
編譯,運行Xcode,以便讓Xcode產生IDEActivityReportStringSegment變化.下面的gif動畫,顯示了上面所說的全部你輸入到LLDB中的命令,看起來以下:
比較控制檯的輸出和Xcode的標題欄的輸出,你能夠明確的獲得:它就是你要尋找的內容!!
到使用Swizzle的時候了
建立一個Objective-C的類NSObject的category,命名爲Rayrolling IDEActivityReportStringSegment
添加下面的代碼到創建的文件 NSObject+Rayrolling_IDEActivityReportStringSegment.m:中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
#import "NSObject+Rayrolling_IDEActivityReportStringSegment.h"
#import "NSObject+MethodSwizzler.h"
#import "Rayrolling.h"
@interface NSObject ()
// 1
- (id)initWithString:(id)arg1 priority:(double)arg2 frontSeparator:(id)arg3 backSeparator:(id)arg4;
@end
@implementation NSObject (Rayrolling_IDEActivityReportStringSegment)
// 2
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[NSClassFromString(@
"IDEActivityReportStringSegment"
) swizzleWithOriginalSelector:@selector(initWithString:priority:frontSeparator:backSeparator:) swizzledSelector:@selector(Rayrolling_initWithString:priority:frontSeparator:backSeparator:) isClassMethod:NO];
});
}
- (id)Rayrolling_initWithString:(NSString *)string priority:(double)priority frontSeparator:(id)frontSeparator backSeparator:(id)backSeparator
{
// 3
static NSArray *lyrics;
// 4
static NSInteger index = 0;
static dispatch_once_t onceToken;
// 5
dispatch_once(&onceToken, ^{
lyrics = @[@
"Never gonna live you up."
,
@
"Never gonna set you down."
];
});
// 6
index = index >= lyrics.count -1 ? 0 : index + 1;
// 7
if
([Rayrolling isEnabled]) {
return
[self Rayrolling_initWithString:lyrics[index] priority:priority frontSeparator:frontSeparator backSeparator:backSeparator];
}
return
[self Rayrolling_initWithString:string priority:priority frontSeparator:frontSeparator backSeparator:backSeparator];
}
@end
|
下面是對上面代碼的一些解釋:
你須要前向聲明私有類的初始化方法initWithString:priority:frontSeparator:backSeparator:,不然編譯器直接就讓你經過不了.
load是一般被用來初始化添加swizzle代碼的地方.
因 爲你使用一個category來交換方法,在category中創建實例變量是稍微有些複雜的.你能夠經過associated objects給一個已存在的類添加實例變量,但這個是你本身應該掌握的內容.在這裏,咱們直接使用一個static NSArray來保持一個對象繼續存在,即便它所在的方法已經執行完畢了.
您可使用相同的static伎倆,使得index也如此。
使用dispatch_once初始化歌詞NSArray
遞增index,若是越界了,就重置它
檢測插件是不是可用狀態,若是是,那就更改string參數,不然,使用默認的參數
正如你所看到的,使用正確的工具能夠幫助你快速定位你想要的東西.然而,要達到這個目的,卻有不止一種方法 - 你將會發現,經過堆(heap)分析照樣能夠定位到你所感興趣的內容
經過堆(heap)來從新尋找IDEActivityView
在本節中,你將會從新搜尋Xcode的IDEActivityView和IDEActivityReportStringSegment,你將會使用一種略微不一樣的方式來達到這一目的.
到目前爲止,你都是經過自上而下的方式來進行定位的:你先找到一個可能的class,而後以某種方式在內存中尋找它的一個實例對象,再查找它的成員方法和屬性.其實,以相反的方向來進行,彷佛也是可行的,就是沿着參考鏈從下而上去尋找目標對象,直到找到它.
很幸運,Xcode中附帶了一些Python腳本,讓你可以作到這點!
結束掉終端中全部的已存在的LLDB和Dtrace的會話,而後重啓Xcode.開始一個新的Xcode和LLDB會話,再一次執行LLDB附着到Xcode上的命令,而後,添加下面的命令腳本:
1
2
3
|
(lldb) pro at -n Xcode
(lldb) command script import lldb.macosx.heap
"malloc_info"
,
"ptr_refs"
,
"cstr_refs"
, and
"objc_refs"
commands have been installed, use the
"--help"
options on these commands
for
detailed help.
|
這個腳本加載了一系列很是有用的方法到LLDB進程中.
malloc_info: 展現當前堆得對象的詳細信息.尤爲是結合MallocStackLogging環境變量的時候及其有用!
ptr_refs: 給它一個堆空間中的一個內存地址,它能夠找到指向這個內存地址的其它對象.
cstr_refs: 參數類型是char * ,查找它在內存中出現的地方.
objc_refs: 找到內存中指定的類的實例
你可能想先了解了解最容易的objc_refs.
在LLDB中,經過如下腳原本找尋全部的IDEActivityView類的實例變量:
1
2
3
4
|
(lldb) objc_refs -o IDEActivityView
Searching
for
all instances of classes or subclasses of
"IDEActivityView"
(isa=0x10fffe650)
0x00007faaee9cbf30: malloc( 304) -> 0x7faaee9cbf30 IDEActivityView.DVTLayerHostingView.NSView.NSResponder.NSObject.isa
[IDEActivityView: 0x7faaee9cbf30](因識別問題,這裏將尖括號替換爲方括號)
|
這個腳本輸出了內存中全部的IDEActivityView類的實例.其中的 -o 參數表示輸出對象,也就是會調用對象的 description 方法
經過這個簡單地額腳本,你就能夠在內存中找屬於指定類的的實例對象.這招也適用於繼承於該對象的子類.
yohunl備註:很遺憾,在我電腦上,這句怎麼都查詢不到
1
2
3
4
|
(lldb) objc_refs -o IDEActivityView
error: libarclite_macosx.a(arclite.o) failed to load objfile
for
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_macosx.a
error: libarclite_macosx.a(arclite.o) failed to load objfile
for
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_macosx.a
Searching
for
all instances of classes or subclasses of
"IDEActivityView"
(isa=0x10ada9328)
|
試了不少次,都沒能獲得所指望的東西,但我相信這個方法是能夠的
經過上一步 objc_refs發現的內存地址,你能夠採用以下的腳原本展開一個IDEActivityView實例的成員變量:
1
2
3
4
5
6
|
(lldb) malloc_info -t 0x7faaee9cbf30
0x00007faaee9cbf30: malloc( 304) -> 0x7faaee9cbf30 IDEActivityView.DVTLayerHostingView.NSView.NSResponder.NSObject.isa (IDEActivityView) *addr = {
DVTLayerHostingView = {
NSView = {
NSResponder = {
...
|
這還不是它能作到的所有!你甚至能夠用它,經過指定的內存地址來得到其它對象的引用:
1
2
3
4
5
6
|
(lldb) ptr_refs 0x7faaee9cbf30
0x00007faaeed9a308: malloc( 16) -> 0x7faaeed9a300 + 8
0x00007faaeedb4148: malloc( 176) -> 0x7faaeedb4140 + 8 IDEActivityViewBackgroundButton.NSButton.NSControl.NSView.NSResponder._nextResponder
0x00007faaeedb4190: malloc( 176) -> 0x7faaeedb4140 + 80 IDEActivityViewBackgroundButton.NSButton.NSControl._aux.NSObject.isa
0x00007faaeedb4928: malloc( 160) -> 0x7faaeedb4920 + 8 NSView.NSResponder._nextResponder
...
|
注意:這些命令,若是結合 im loo -rn {classmethod regex} 方式來查詢,甚至能夠幫助你快速的定位到,而且探查到在內存中存活的該類的全部實例.固然了,嘗試以前,請肯定你的LLDB仍是暫停狀態,不然的話,這些命令是不會起做用的.
你設置能夠更進一步,有一個環境變量MallocStackLogging,它能夠在你初始化任何一個對象的時候用於棧(stack)回溯.雖然不少引用均可以指向同一個對象,可是它能夠指出來,哪一個纔是這個對象的」最終擁有者」. 這段還要再分析一下
因爲上文的LLDB已經附着(attach)在xcode上,因此你要先殺掉Xcode進程,再經過帶上MallocStackLogging環境參數的LLDB來重啓它:
1
2
3
4
|
(lldb) pro kill
Process 65196 exited
with
status = 9 (0x00000009)
(lldb) pro launch -v MallocStackLogging=1 -n
Process 67920 launched:
'/Applications/Xcode.app/Contents/MacOS/Xcode'
(x86_64)
|
注意:若是你比較懶,不斷的殺掉LLDB/Xcode會話,launchctl是一個更加方便的啓用環境變量MallocStackLogging的方式:
1
|
launchctl setenv MallocStackLogging 1
|
這個命令將經過launch建立的每個進程的MallocStackLogging都設置成了true.因此你要記得,經過以下的命令來移除它們:
1
|
launchctl unsetenv MallocStackLogging
|
爲了測試上面的內容,首先,確保你的Xcode的Edit菜單下的菜單項是 以下圖所示的 Enable Rayrolling :
如今,打開任何一個Xcode工程,保證源碼編輯窗口是打開的,編譯,運行工程.伴隨着工程的運行,你將會看到在編譯完成和運行完成的時候,IDEActivityView(僞裝你不知道此時此刻,誰調用它)改變它的內容.
暫停執行這個Xcode工程,若是須要,使用下面的命令來從新加載LLDB的heap(堆)方法集:
1
2
3
|
(lldb) pro i
(lldb) command script import lldb.macosx.heap
"malloc_info"
,
"ptr_refs"
,
"cstr_refs"
, and
"objc_refs"
commands have been installed, use the
"--help"
options on these commands
for
detailed help.
|
提醒:若是你對屢次輸入這個命令感到厭煩,你能夠去網上了解下怎麼在 ~/.lldbinit 方法中創建 command aliases (命令的別名,或者縮寫).這個在 ~/.lldbinit 文件的內容,在每次建立一個LLDB的會話實例的時候,都會被加載.
如今,使用cstr_ref方法,給它傳遞Xcode標題欄的字符串內容.舉個例子,若是這個工程的名字叫Rayrolling,那麼你的Xcode的標題欄上顯示的應該是諸如:」Running Rayrolling : Rayrolling:」此類的字符串:
1
2
3
4
5
6
|
(lldb) cstr_ref
"Running Rayrolling : Rayrolling"
0x0000000118cb21d0: malloc( 32) -> 0x118cb21d0
0x000000012079ae21: malloc( 48) -> 0x12079ae10 + 17 __NSCFString1 bytes after __NSCFString
0x00000001207a3bb3: malloc( 64) -> 0x1207a3b90 + 35 __NSCFString19 bytes after __NSCFString
0x00000001223834d1: malloc( 48) -> 0x1223834c0 + 17 __NSCFString1 bytes after __NSCFString
0x000000011f9126a1: malloc( 48) -> 0x11f912690 + 17 __NSCFString1 bytes after __NSCFString
|
正如你所看到的那樣,主要的參考信息通常都是 NSString類型的參考,多數狀況下,你並不能從字符串自己獲得更多的信息.因此你可能想了解這樣字符串是怎樣被建立的?由於有MallocStackLogging,作到這點,至關容易!
1
|
(lldb) cstr_ref
"Running Rayrolling : Rayrolling"
-s
|
這個腳本輸出堆棧幀的信息,當這個char * 被建立的時候.
經過全部的堆棧信息去跟蹤實例變量,因爲他們都是在內存中,內存地址的數量和其在內存中的相對位置都有可能不一樣,這些都取決於你和Xcode的互動方式的不一樣而不一樣.
經過閱讀分析堆棧的跟蹤信息,你將會得到一組類,這些類就是負責Xcode中這方面的.
舉例來講,取決於你運行的這些LLDB查詢命令,你可能會看到這些class,如: IDEActivityScrollingTextLayer , IDERunOperation , IDEActivityView 等等諸如此類的class.
你還可使用 ptr_ref 來做用於 cstr_ref 輸出的信息,來看那些對象保留了這個string對象的引用.
繁雜的隨機探索技巧
在Xcode中尋找正確的api是很複雜的,由於你是想在成千上萬的類和方法中尋找符合條件的幾個類而已.
使用LLDB的正則表達式,能夠幫助你更好的找到它們.某些時候,最好的獲取信息的地方是代碼中使用的單例(Singleton),查看Xcode中存在的單例,看看是否值得進一步深刻的單例.
當LLDB附着到Xcode,而且處於暫停狀態時候,輸入如下命令:
1
|
(lldb) i loo -rn
"\+\[IDE.*\ shared"
|
這條命令將會查找以IDE開頭的類名,而且以」shared」開頭的類方法.這條命令等價於 target modules lookup -rn "\+\[IDE.*\ shared",-r是指後面的是正則表達式
另一個吸引力的替代方案是:
1
|
(lldb) i loo -rn
"\+\[[A-Za-z]*\ [A-Za-z]*\]"
|
它將會輸出全部的沒有參數的類方法.
正 如你前面看到的,你能夠很容易的找出你剛興趣的類,經過給一個名字給上面提到的Python腳本,就能夠搜索到.另外還有一個流行的工具,能夠用來搜索類 和類方法,這個工具的名字叫class-dump,它能夠在 這裏下載.它是LLDB命令im loo -rn {regex} 的另外一種替代方式,由於它(Class-dump)能夠輸出更加整潔,清晰的頭文件形式的輸出.
固然了,class-dump也有小缺點, 它的缺點就是你必須限制你的搜索在指定的framework或者plugin中,而LLDB的image lookup 命令搜索的是整個的Xcode進程中的framework和plugin!也就是image lookup的搜索範圍更大,更廣.
下一步該作什麼?
這裏是第二部分最後完成的domo工程.
在這個教程中,你學習到了最基本的DTrace技能,而且學習和使用了一些你能夠獲得的高級LLDB方法.
若是你想了解更多的關於DTrace的知識,你能夠閱讀另外一篇obcj.io上的優秀文章,還有就是官方的 DTrace文檔
在本系列教程的第三部分,咱們將再一次關注 彙編,你將會學到另外一種靈巧的工具,它的名字叫 Cycript.