Respuestas:
void *
significa "una referencia a un poco de memoria aleatoria con contenido sin tipo / desconocido"
id
significa "una referencia a algún objeto Objective-C aleatorio de clase desconocida"
Hay diferencias semánticas adicionales:
En los modos GC Only o GC Supported, el compilador emitirá barreras de escritura para referencias de tipo id
, pero no para tipo void *
. Al declarar estructuras, esto puede ser una diferencia crítica. Declarar iVars como void *_superPrivateDoNotTouch;
causará la cosecha prematura de objetos si en _superPrivateDoNotTouch
realidad es un objeto. No hagas eso.
intentar invocar un método en una referencia de void *
tipo generará una advertencia de compilación.
intentar invocar un método en un id
tipo solo advertirá si el método al que se llama no se ha declarado en ninguna de las @interface
declaraciones vistas por el compilador.
Por lo tanto, uno nunca debe referirse a un objeto como a void *
. Del mismo modo, uno debe evitar el uso de una id
variable escrita para referirse a un objeto. Utilice la referencia de tipo de clase más específica que pueda. Incluso NSObject *
es mejor que id
porque el compilador puede, al menos, proporcionar una mejor validación de las invocaciones de métodos contra esa referencia.
El uso común y válido de void *
es como una referencia de datos opaca que se pasa a través de alguna otra API.
Considere el sortedArrayUsingFunction: context:
método de NSArray
:
- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context;
La función de clasificación se declararía como:
NSInteger mySortFunc(id left, id right, void *context) { ...; }
En este caso, el NSArray simplemente pasa todo lo que pasa como context
argumento al método a través del context
argumento. Es un fragmento opaco de datos del tamaño de un puntero, en lo que respecta a NSArray, y puede usarlo para el propósito que desee.
Sin una característica de tipo de cierre en el idioma, esta es la única forma de llevar un trozo de datos con una función. Ejemplo; si desea que mySortFunc () clasifique condicionalmente como mayúsculas o minúsculas, sin dejar de ser seguro para subprocesos, debería pasar el indicador de mayúsculas y minúsculas en el contexto, probablemente arrojando al entrar y salir.
Frágil y propenso a errores, pero la única forma.
Los bloques resuelven esto: los bloques son cierres para C. Están disponibles en Clang - http://llvm.org/ y son penetrantes en Snow Leopard ( http://developer.apple.com/library/ios/documentation/Performance /Reference/GCD_libdispatch_Ref/GCD_libdispatch_Ref.pdf ).
id
responde. Un id
puede referirse fácilmente a una instancia de una clase que no es inherente a NSObject
. Sin embargo, en términos prácticos, su declaración coincide mejor con el comportamiento del mundo real; no puede mezclar <NSObject>
clases que no se implementen con Foundation API y llegar muy lejos, ¡eso definitivamente es seguro!
id
y los Class
tipos se tratan como puntero de objeto retenible en ARC. Entonces, la suposición es cierta al menos bajo ARC.
id es un puntero a un objeto C objetivo, donde como void * es un puntero a cualquier cosa.
id también desactiva las advertencias relacionadas con la llamada a métodos desconocidos, por ejemplo:
[(id)obj doSomethingWeirdYouveNeverHeardOf];
no dará la advertencia habitual sobre métodos desconocidos. Por supuesto, generará una excepción en tiempo de ejecución a menos que obj sea nulo o realmente implemente ese método.
A menudo debe usar, NSObject*
o lo id<NSObject>
prefiere id
, lo que al menos confirma que el objeto devuelto es un objeto Cocoa, por lo que puede usar métodos seguros como retención / liberación / liberación automática en él.
Often you should use NSObject*
lugar de id
. Al especificar NSObject*
, en realidad está diciendo explícitamente que el objeto es un NSObject. Cualquier llamada de método al objeto dará como resultado una advertencia, pero no habrá una excepción de tiempo de ejecución mientras ese objeto realmente responda a la llamada de método. La advertencia es obviamente molesta, así que id
es mejor. Por lo general, puede ser más específico, por ejemplo id<MKAnnotation>
, diciendo que, en este caso, sea cual sea el objeto, debe cumplir con el protocolo MKAnnotation.
Si un método tiene un tipo de id
retorno, puede devolver cualquier objeto Objective-C.
void
significa que el método no devolverá nada.
void *
Es solo un puntero. No podrá editar el contenido en la dirección a la que apunta el puntero.
id
es un puntero a un objeto Objective-C. void *
es un puntero a cualquier cosa . Podría usar en void *
lugar de id
, pero no se recomienda porque nunca obtendría advertencias del compilador para nada.
Es posible que desee ver stackoverflow.com/questions/466777/whats-the-difference-between-declaring-a-variable-id-and-nsobject y unixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs -id.html .
void *
variables escritas definitivamente pueden ser el objetivo de las invocaciones de métodos, es una advertencia, no un error. No sólo eso, usted puede hacer esto: int i = (int)@"Hello, string!";
y el seguimiento con: printf("Sending to an int: '%s'\n", [i UTF8String]);
. Es una advertencia, no un error (y no es exactamente recomendado, ni portátil). Pero la razón por la que puedes hacer estas cosas es todo básico C.
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
El código anterior es de objc.h, por lo que parece que id es una instancia de objc_object struct e isa pointer puede vincularse con cualquier objeto Objective C Class, mientras que void * es solo un puntero sin tipo.
Entiendo que id representa un puntero a un objeto, mientras que void * puede apuntar a cualquier cosa realmente, siempre y cuando lo conviertas en el tipo que deseas usar como
Además de lo que ya se dijo, hay una diferencia entre los objetos y los punteros relacionados con las colecciones. Por ejemplo, si desea poner algo en NSArray, necesita un objeto (del tipo "id"), y no puede usar un puntero de datos sin procesar allí (del tipo "void *"). Puede usar [NSValue valueWithPointer:rawData]
para convertir void *rawDdata
al tipo "id" para usarlo dentro de una colección. En general, "id" es más flexible y tiene más semántica relacionada con los objetos adjuntos. Hay más ejemplos que explican el tipo de identificación del Objetivo C aquí .
id
se supone que responde a-retain
y-release
, mientras que avoid*
es completamente opaco para la persona que llama. No puede pasar un puntero arbitrario a-performSelector:withObject:afterDelay:
(retiene el objeto), y no puede asumir que+[UIView beginAnimations:context:]
retendrá el contexto (el delegado de animación debe mantener la propiedad del contexto; UIKit retiene el delegado de animación).