objective-c cocoa objective-c-runtime

¿Por qué no deberías usar objc_msgSend() en Objective C?



objective-c cocoa (3)

La Guía de tiempo de ejecución de Objective C de Apple indica que nunca debe usar objc_msgSend () en su propio código y recomienda usar methodForSelector: en su lugar. Sin embargo, no proporciona ninguna razón para esto.

¿Cuáles son los peligros de llamar a objc_msgSend () en su código?


Razón # 1: Estilo malo: es redundante e ilegible.

El compilador genera automáticamente llamadas a objc_msgSend() (o alguna variante del mismo) cuando encuentra expresiones de mensajería de Objective-C. Si sabe que la clase y el selector se enviarán en tiempo de compilación, no hay razón para escribir

id obj = objc_msgSend(objc_msgSend([NSObject class], @selector(alloc)), @selector(init));

en lugar de

id obj = [[NSObject alloc] init];

Incluso si no conoce la clase o el selector (o ambos), es aún más seguro (al menos el compilador tiene la posibilidad de advertirle si está haciendo algo potencialmente desagradable / incorrecto) obtener un puntero de función correctamente escrito para la implementación en sí y usar ese puntero de función en su lugar

const char *(*fptr)(NSString *, SEL) = [NSString instanceMethodForSelector:@selector(UTF8String)]; const char *cstr = fptr(@"Foo");

Esto es especialmente cierto cuando los tipos de argumentos de un método son sensibles a las promociones predeterminadas: si lo son, entonces no desea pasarlos a través de los diversos argumentos que objc_msgSend() toma, porque su programa invocará rápidamente un comportamiento indefinido.

Razón # 2: peligroso y propenso a errores.

Observe la parte "o alguna variante del mismo" en # 1. No todos los envíos de mensajes utilizan la función objc_msgSend() sí. Debido a complicaciones y requisitos en el ABI (en la convención de funciones de llamada, en particular), hay funciones separadas para devolver, por ejemplo, valores o estructuras de punto flotante. Por ejemplo, en el caso de un método que realiza algún tipo de búsqueda (subcadenas, etc.), y devuelve una estructura NSRange , dependiendo de la plataforma, puede ser necesario usar la versión que devuelve la estructura de la función de mensajería:

NSRange retval; objc_msgSend_stret(&retval, @"FooBar", @selector(rangeOfString:), @"Bar");

Y si se equivoca (por ejemplo, si utiliza la función de mensajería inapropiada, mezcla los punteros al valor de retorno y a self , etc.), es probable que su programa se comporte de forma incorrecta y / o se bloquee. ( Y lo más probable es que se equivoque, porque ni siquiera es tan simple: no todos los métodos que devuelven una struct usan esta variante, ya que las estructuras pequeñas caben en uno o dos registros del procesador, eliminando la necesidad de usar la pila como el lugar de el valor de retorno. Es por eso que, a menos que usted sea un hacker ABI incondicional, prefiere dejar que el compilador haga su trabajo, o que haya dragones.


Preguntas "¿cuáles son los peligros?" y @ H2CO3 ha enumerado algunos finales con "a menos que seas un pirata informático de ABI" ...

Al igual que con muchas reglas, hay excepciones (y posiblemente algunas más bajo ARC). Por lo tanto, su razonamiento para usar msgSend debe ir de la siguiente manera:

[1] Creo que debería usar msgSend , no .

[2] Pero tengo un caso aquí ... - probablemente no, busque otra solución .

...

[10] Realmente creo que debería usarlo aquí, piénsalo de nuevo .

...

[100] Realmente, esto parece un caso para msgSend , ¡no puedo ver ninguna otra solución! Bien, ve a leer Document.m en el ejemplo de código de TextEdit de Apple . ¿ Sabes por qué usaron msgSend ? ¿Estás seguro ... piensa otra vez ...

...

[1000] Entiendo por qué Apple lo usó, y mi caso es similar ... Ha encontrado y comprendido la excepción que prueba que la regla coincide con su caso, ¡ úselo !

HTH


Puedo hacer un caso. Usamos msgSend en uno de nuestros archivos C ++ que está en un proyecto multiplataforma (Windows, Mac y Linux). Lo usamos para contar una referencia en el respaldo (el código compartido) que se usa más adelante para pasar de frontend a backend y viceversa. Caso muy especial, la verdad.