2011-07-16 27 views
5

Estoy tratando de subclasificar una cierta clase y anular algunos de sus métodos privados. Esto es peligroso, podría fallar, no podría pasar la revisión de Apple (en la AppStore), podría tener efectos terribles, etc., pero esto es solo para propósitos experimentales/educativos :)Obteniendo el tipo de argumento en un método

Por ejemplo, digamos que quiero saber el tipo del primer argumento del método de instancia keyboardInput:shouldInsertText:isMarkedText: de 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); 

Sin embargo, en la consola solo me dan [email protected]_. Creo que @ significa objeto pero ¿un objeto de qué clase? (Pensé que obtendría el nombre de la clase del argumento) ¿Es posible obtener la clase de ese parámetro utilizando otro objective-c runtime functions o cualquier otro medio?

PD: En el ejemplo utilicé una clase de toque Cocoa, pero podría haber intentado exactamente lo mismo con una clase de Cocoa. Entonces esto no debería ser específico para iOS.

resuelto:

que he probado en el simulador de lo que Dave sugirió, funcionó!

(gdb) info all-registers me dio una larga lista de valores, ...

((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 

lo cual tiene sentido ya que la clave Presioné era una sola "t" por lo que no estaba marcado de texto ("0x0") por lo que está bien pensar que el primer argumento debería ser del tipo UIWebDocumentView.

Solo una pequeña cosa (irrelevante en este caso), ¿cómo podría obtener el nombre del método de un SEL en gbd? por ejemplo $5?

+0

El 'SEL' * es * el nombre del método. Use la función 'sel_getName' para obtener el selector como una cadena C; ver también http://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/. También hay 'NSStringFromSelector', que hace exactamente lo que dice, y es parte de (y documentada en) Foundation. –

Respuesta

6

Suponiendo que esto es para los intereses académicos solamente ....

Una forma es posible descubrir es romper en el símbolo de GDB e inspeccionar los parámetros. ¿Sabes qué general tipo son (es decir, float vs object, etc.), por lo que simplemente puede utilizar el comando p para imprimirlos.

Para establecer el punto de interrupción, puede hacer:

  1. En la consola GDB, el tipo b -[UITextView keyboardInput:shouldInsertText:isMarkedText:]
  2. En Xcode, añadir un nuevo punto de ruptura simbólica para -[UITextView keyboardInput:shouldInsertText:isMarkedText:]

El momento para golpear el punto de quiebre, no vas a ver nada útil (por supuesto). Pero puede imprimir los argumentos en este punto, porque conoce sus tamaños, y por lo tanto, dónde deberían estar en los registros.

Para obtener más información sobre los registros en los que se encuentran, consulte this handy blog post. También puede hacer algo como info all-registers para imprimir toda la información en todos los registros, etc.


actualización

Si desea imprimir el nombre del selector, se puede usar un poco truco, y es que los selectores son más o menos * char* s:

p (char*)$5 

imprimirá:

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

¡Gracias, funcionó! Tengo una última y poca duda, sin embargo :) (Lea la respuesta actualizada) – nacho4d

+0

@ nacho4d respuesta actualizada –

+0

¡increíble! gran respuesta :) – nacho4d

0

@ de hecho significa "objeto" pero no se puede averiguar de qué clase se trata. Esto no se guarda en el segmento __OBJC del binario y, por lo tanto, no está disponible para usted a menos que tenga el archivo de encabezado original.

Tienes que adivinarlo. :)

9

No se puede distinguir la clase del argumento de la introspección de la interfaz. Todos los id se crean iguales en Objective-C, una vez que se ha compilado. Sin embargo, puede preguntar a una instancia específica es -class. Por lo tanto, si intenta realizar una ingeniería inversa de un método interno, deberá cambiar el método, hacer que se lo llame y, a continuación, introspectir los objetos que se le pasan. Google "swizzling de métodos" para mucha discusión sobre cómo inyectar tu código en cualquier método Objective-C.

+0

+1 porque es más cierto que mi respuesta. –

+0

Pensé en swizzling, pero en este caso, si no sé el tipo del primer argumento, las implementaciones de swizzling no funcionarán, creo. – nacho4d

+0

Sabes que es un 'id'. Eso es todo lo que necesitas. Recuerde, todos los 'id' se crean iguales después de la compilación. Dicho esto, me gusta @Dave la solución de DeLong mejor para este problema. –

Cuestiones relacionadas