2011-07-16 113 views
5

我想要繼承某個類並覆蓋它的一些私有方法。這是危險的,可能會失敗,無法通過蘋果的審查(在AppStore),可能會有可怕的影響等,但這只是爲了實驗/教育的目的:)在方法中獲取參數類型

例如,讓我們說我想知道類型實例方法UITextViewkeyboardInput:shouldInsertText:isMarkedText:的第一個參數:

SEL selector = @selector(keyboardInput:shouldInsertText:isMarkedText:); 
Class class = [UITextView class]; 
Method method = class_getInstanceMethod(class, selector); 
char *arg = method_copyArgumentType(method, 0); 
printf("_%s_\n", arg); 
free(arg); 

但是在控制檯中我只得到[email protected]_。 我認爲@意味着對象但什麼類的對象? (我想我會得到參數類的名稱)是否有可能使用其他objective-c runtime functions或其他任何意思得到該參數的類? PS:在這個例子中,我使用了一類可可觸摸,但我可以嘗試與一類可可完全相同。所以這不應該特定於iOS。

解決:

我在戴夫建議模擬器嘗試,它的工作!

(gdb) info all-registers給了我價值的一個長長的清單,...

((gdb) po *(id*)($ebp + 8) 
<MyTextView: 0x5911270; baseClass = UITextView; frame = (80 70; 240 323); text = 'Lorem ipsum dolor sit er ...'; clipsToBounds = YES; autoresize = RM+BM; layer = <CALayer: 0x5c0c7d0>; contentOffset: {0, 0}> 

(gdb) p *(SEL*)($ebp + 12) 
$5 = (SEL) 0xbd19 

(gdb) po *(id*)($ebp + 16) 
<UIWebDocumentView: 0xa02f000; frame = (0 0; 240 457); text = 'Lorem ipsum dolor sit er ...'; opaque = NO; userInteractionEnabled = NO; layer = <UIWebLayer: 0x5c35070>> 

(gdb) po *(id*)($ebp + 20) 
t 

(gdb) p *(id*)($ebp + 24) 
$6 = (id) 0x0 

這是有道理的,因爲我按下的鍵是一個「T」,所以它沒有標註文字(「爲0x0」),所以我可以認爲第一個參數應該是UIWebDocumentView

只是一件小事(在這種情況下無關緊要),我怎麼能從gbd的SEL中獲得方法名?例如$5

+0

'SEL' *是方法名稱。使用'sel_getName'函數將選擇器作爲C字符串;另請參閱http://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/。還有'NSStringFromSelector',它完全按照它所說的,是基礎的一部分(並且記錄在其中)。 –

回答

6

假設這是對學術興趣只有 ....

一個你能找到的出路就是打破在GDB的符號,並檢查參數。你知道什麼一般他們是什麼樣的(即,float vs object等),所以你可以簡單地使用正確的p命令來打印它們。

設置斷點,你可以這樣做:

  1. 在gdb的控制檯,輸入b -[UITextView keyboardInput:shouldInsertText:isMarkedText:]
  2. 在Xcode中,添加一個新的象徵斷點-[UITextView keyboardInput:shouldInsertText:isMarkedText:]

當你真正打斷點,你不會看到任何有用的東西(當然)。但你可以在這一點上打印出來的參數,因爲你知道它們的大小,因此它們應該在寄存器中。

欲瞭解更多關於他們在哪個寄存器的信息,請查看this handy blog post。你也可以這樣做info all-registers打印出所有的信息,所有的寄存器等


更新

如果你要打印選擇的名稱,你可以使用一個小伎倆,這是選擇更或多或少* char* S:

p (char*)$5 

會打印:

${some number} = 0x{some address} "keyboardInput:shouldInsertText:isMarkedText:" 
+0

謝謝,它工作!我有一個最後,有點疑問,雖然:)(請閱讀更新的答案) – nacho4d

+0

@ nacho4d更新回答 –

+0

真棒!很好的回答:) – nacho4d

0

@的確表示「對象」,但您無法找到它是什麼類。這不會保存在二進制文件的__OBJC段中,因此除非具有原始頭文件,否則不可用。

你必須猜測它。 :)

9

你不能通過接口的內省來告訴參數的類。一旦你編譯完成,所有的id在Objective-C中都是相同的。但是,您可以詢問具體的實例-class。所以如果你想要反向設計一個內部方法,你需要調用方法,調用它,然後反思你傳遞的對象。關於如何將代碼注入任何Objective-C方法的大量討論,Google「方法混合」。

+0

+1,因爲它比我的回答更真實。 – 2011-07-16 02:15:08

+0

我想到了混搭,但是在這種情況下,如果我不知道第一個參數的類型,那麼我認爲混合實現將不起作用。 – nacho4d

+0

你知道這是一個'id'。這就是你需要的。請記住,編譯後所有的'id'都是相等的。也就是說,我喜歡@Dave DeLong的解決方案,以解決這個問題。 –