2009-08-21 13 views

Respuesta

17

Todo tiene que ver con la C llamando a ABI.

Considere estos métodos:

- (id)initWithFormat:(NSString *)format, ...; 
+ (id)arrayWithObjects:(id)firstObj, ...; 
+ (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...; 

El ... indica al compilador que un número variable de argumentos de cualquier tipo puede estar presente. No hay forma de que el compilador sepa qué tipo de estos deben ser (las definiciones reales tienen marcadores que ayudan con eso).

Ahora, considere los tres métodos. Los tres tienen requisitos muy diferentes para lo que podría estar presente en la lista de argumentos variables. Una matriz debe ser un grupo de objetos seguido de un nil. El diccionario requiere un grupo de pares de objetos seguidos de un nil. Finalmente, el método de cadena requiere un grupo de argumentos que coincidan con los tipos en la cadena de formato.

Todos estos comportamientos están directamente relacionados con el método invocado y, si el autor de una API decidió usar "difícil de usar", el comportamiento de decodificación de los argumentos variables podría modificarse en tiempo de ejecución, solo para hacer vida difícil

En pocas palabras: El C ABI no tiene una sintaxis que permita especificar que un método o función toma un número variable de argumentos con cualquier tipo de conjunto de restricciones sobre los argumentos o su terminación.

Objective-C podría cambiar las reglas solo para declaraciones de métodos & invocaciones, pero eso no ayudaría con las funciones C o C++, las cuales Objective-C debe seguir siendo compatible.

+0

Parece que el compilador podría agregar transparentemente un nil final a [matriz NSArrayWithObjects: a, b, c]. Eso dejaría un problema que no se permitiría como argumento. Incluso podríamos hacer esto ahora. Las personas que ya incluyen el nil obtendrían [NSArray arrayWithObjects: a, b, c, nil, nil], lo que creo que no es fatal. – Ken

+2

Ken, colocando casos especiales en el compilador para clases particulares como NSArray abre una enorme lata de gusanos. ¿Qué pasa si subclasifica a NSArray? – NSResponder

+0

@Ken: no se puede agregar 'nil' a' NSArray' de todos modos (en su lugar, tiene que usar 'NSNull'). – mipadi

3

Cualquier función varargs requiere de alguna manera saber cuántos parámetros están presentes; si no termina, solo necesitaría algo más. En este caso, la alternativa obvia es la longitud, pero luego deberá actualizar el argumento de longitud cada vez que cambie la cantidad de elementos en la matriz, y eso podría ser engorroso o incluso peor.

Supongo que tiene una matriz que puede contener nil?

+1

NSArray no puede contener nada; debe usar NSNull para representarlo. Supongo que solo le han picado los molestos errores que pueden surgir al olvidar el terminador nulo. – Chuck

+0

No es tanto ser pequeño olvidando el terminador nil, es solo que estoy acostumbrado a otros lenguajes como Java y Ruby que permiten varargs sin un terminador nulo. Tal vez lo que estoy preguntando es por qué Obj-C requiere un terminador nil para varargs? ¿Por qué no puede el compilador simplemente "hacer lo correcto" para [NSArray arrayWithObjects: @ "foo", @ "bar"]? Mi pregunta es más una cuestión de curiosidad sobre por qué el lenguaje requiere esto. Es una verruga obvia cuando proviene de otros idiomas. Es así porque así es como varargs en C funciones? – pjb3

+3

I _believe_ object-c varargs se crean en las variables estándar de C que no superan recuentos de argumentos. Posiblemente obj-c podría haberlo hecho en su implementación original, pero ahora está obligado por la compatibilidad ABI. – olliej

2

@bbum proporciona algunos detalles técnicos geniales. Aquí hay algunas ideas sobre la práctica "¿por qué no lo arreglan?"

Recuerde: C es solo ensamblador y Obj-C es solo C ... Podría ser rediseñado, por supuesto, pero casi no hay presión para hacerlo. Aún no puedes poner nil en una matriz (eso requeriría un gran cambio). El compilador ahora te advierte si olvidas el nil, por lo que no es algo de lo que los desarrolladores se quejan mucho. La compensación es mantener el lenguaje mucho (¡mucho!) Más simple que su pariente, obteniendo los beneficios de décadas de optimizaciones del compilador C y compatibilidad garantizada con el código C.

Una cosa que debería quedar clara en la discusión de @ bbum: NSArray no es una función de lenguaje de Objective-C. Las matrices C son una función de idioma, pero las NSArrays son solo otro objeto, no diferente de los objetos que se escriben.

2

Existen varias soluciones, pero mi favorito actual es bueno para aplicaciones más que para frameworks lanzados. Acepte un NSArray en su método en lugar de "...", y luego llénelo con la macro de conveniencia a continuación, que se coloca en su archivo de prefijo o en el encabezado de la herramienta.

#define $ array (objs ...) [NSArray arrayWithObjects: objs, nil]

Permitirá múltiples argumentos de longitud variable bien etiquetados y se liberará del patrón arcaico de tener que usar el primer argumento y luego va_list y sus hermanos a favor de un bucle for-in o las muchas otras herramientas de recolección disponibles.

[self findMatchingSetAndAlert: @ "title" lazos: $ array (tie1, tie2, tie3) camisetas: $ array (shirt1, shirt2, shirt3, shirt4)];

Si alguien sabe cómo implementar una lista no delimitada por nulos, como stringWithFormat, ¡por favor háganoslo saber! Utiliza atributos y macros o algo diseñado específicamente para el formateo, pero esos se implementan de alguna manera.

+0

Mi respuesta es bastante antigua y las macros ya no son necesarias como una muleta. La sintaxis moderna de Objective C ayuda mucho, ya que ahora puedes crear una matriz como '@ [@" firstString ", @" secondString "]'. Ahora podemos preocuparnos por los tipos en lugar de la ceremonia de sintaxis. –

Cuestiones relacionadas