在方法中获取参数类型

nac*_*o4d 5 iphone cocoa objective-c

我试图子类化某个类并覆盖它的一些私有方法.这很危险,可能会失败,无法通过Apple的评论(在AppStore中),可能会产生可怕的影响等,但这只是为了实验/教育目的:)

例如,假设我想知道实例方法keyboardInput:shouldInsertText:isMarkedText:的第一个参数的类型UITextView:

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);
Run Code Online (Sandbox Code Playgroud)

但是在控制台中我只能得到_@_.我认为@意味着对象而不是什么类的对象?(我以为我会得到参数类的名称)是否有可能使用其他Objective-c运行时函数或任何其他方法获取该参数的类?

PS:在这个例子中,我使用了一类Cocoa touch,但我可以尝试使用一类Cocoa完全相同.所以这不应该是针对iOS的.

解决了:

我在模拟器中尝试了Dave的建议,它有效!

(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
Run Code Online (Sandbox Code Playgroud)

这是有道理的,因为我按下的键是单个"t"所以它没有标记文本("0x0")所以我可以认为第一个参数应该是类型UIWebDocumentView.

只是一件小事(在这种情况下无关紧要),我如何从gbd中的SEL获取方法名称?比如$5

Rob*_*ier 9

你不能从界面的内省中告诉类的参数.id一旦编译完成,所有在Objective-C中都是相同的.但是,您可以询问具体的实例-class.因此,如果您正在尝试对内部方法进行反向工程,则需要调整方法,调用它,然后内省您传递的对象.关于如何将代码注入任何Objective-C方法的大量讨论,谷歌"方法调整".


Dav*_*ong 6

假设这是对学术的兴趣 ....

您可以找到的一种方法是打破GDB中的符号并检查参数.你知道一般他们是那种(即,floatVS object等),所以你可以简单地使用正确的p命令来打印.

要设置断点,您可以执行以下操作:

  1. 在gdb控制台中,键入 b -[UITextView keyboardInput:shouldInsertText:isMarkedText:]
  2. 在Xcode中,添加一个新的符号断点 -[UITextView keyboardInput:shouldInsertText:isMarkedText:]

当你真正达到断点时,你不会看到任何有用的东西(当然).但是你可以在这一点上打印出这些参数,因为你知道它们的大小,以及它们应该在寄存器中的位置.

有关他们所在的寄存器的更多信息,请查看这篇方便的博客文章.您还可以执行类似info all-registers打印所有寄存器等所有信息的操作.


更新

如果你想打印选择器的名称,你可以使用一个小技巧,即选择器或多或少*char*s:

p (char*)$5
Run Code Online (Sandbox Code Playgroud)

将打印:

${some number} = 0x{some address} "keyboardInput:shouldInsertText:isMarkedText:"
Run Code Online (Sandbox Code Playgroud)