Risposte:
void *
significa "un riferimento ad una parte casuale della memoria con contenuti non tipizzati / sconosciuti"
id
significa "un riferimento ad un oggetto casuale Objective-C di classe sconosciuta"
Ci sono ulteriori differenze semantiche:
In modalità Solo GC o GC supportate, il compilatore emetterà barriere di scrittura per riferimenti di tipo id
, ma non per tipo void *
. Nel dichiarare le strutture, questa può essere una differenza critica. Dichiarare iVars come questo void *_superPrivateDoNotTouch;
causerà la raccolta prematura di oggetti se in _superPrivateDoNotTouch
realtà è un oggetto. Non farlo.
il tentativo di invocare un metodo su un riferimento di void *
tipo genererà un avviso del compilatore.
il tentativo di invocare un metodo su un id
tipo avviserà solo se il metodo chiamato non è stato dichiarato in nessuna delle @interface
dichiarazioni viste dal compilatore.
Pertanto, non si dovrebbe mai riferirsi a un oggetto come a void *
. Allo stesso modo, si dovrebbe evitare di usare una id
variabile tipizzata per fare riferimento a un oggetto. Usa il riferimento tipizzato di classe più specifico che puoi. Anche NSObject *
è meglio che id
perché il compilatore può, almeno, fornire una migliore convalida delle invocazioni del metodo rispetto a quel riferimento.
L'utilizzo comune e valido di void *
è come riferimento di dati opachi che viene passato attraverso qualche altra API.
Considera il sortedArrayUsingFunction: context:
metodo di NSArray
:
- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context;
La funzione di ordinamento sarebbe dichiarata come:
NSInteger mySortFunc(id left, id right, void *context) { ...; }
In questo caso, NSArray passa semplicemente qualsiasi cosa passi come context
argomento al metodo come context
argomento. È un blocco opaco di dati di dimensioni puntatore, per quanto riguarda NSArray, e sei libero di usarlo per qualsiasi scopo tu voglia.
Senza una funzionalità di tipo di chiusura nella lingua, questo è l'unico modo per trasportare un pezzo di dati con una funzione. Esempio; se si desidera che mySortFunc () venga ordinato in modo condizionale come case sensitive o case sensitive, pur essendo comunque thread-safe, si passerà l'indicatore sensibile al maiuscolo / minuscolo nel contesto, probabilmente lanciandosi lungo la strada per entrare e uscire.
Fragile e soggetto a errori, ma l'unico modo.
I blocchi risolvono questo problema - I blocchi sono chiusure per C. Sono disponibili in Clang - http://llvm.org/ e sono diffusi in Snow Leopard ( http://developer.apple.com/library/ios/documentation/Performance /Reference/GCD_libdispatch_Ref/GCD_libdispatch_Ref.pdf ).
id
risponde. Un id
può facilmente fare riferimento a un'istanza di una classe che non è inerente NSObject
. In pratica, tuttavia, la tua affermazione si adatta meglio al comportamento del mondo reale; non puoi mescolare <NSObject>
classi non implementabili con l'API Foundation e andare molto lontano, questo è sicuramente certo!
id
e Class
tipi vengono trattati come puntatore a oggetto conservabile in ARC. Quindi il presupposto è vero almeno sotto ARC.
id è un puntatore a un oggetto C obiettivo, dove void * è un puntatore a qualsiasi cosa.
id disattiva anche gli avvisi relativi alla chiamata di mthod sconosciuti, quindi ad esempio:
[(id)obj doSomethingWeirdYouveNeverHeardOf];
non darà il solito avvertimento su metodi sconosciuti. Ovviamente genererà un'eccezione in fase di esecuzione a meno che obj non sia nullo o non applichi davvero quel metodo.
Spesso dovresti usare NSObject*
o id<NSObject>
preferire id
, il che conferma almeno che l'oggetto restituito è un oggetto Cocoa, quindi puoi tranquillamente usare metodi come fidelizzare / rilasciare / rilasciare automaticamente su di esso.
Often you should use NSObject*
invece di id
. Specificando, NSObject*
stai effettivamente dicendo esplicitamente che l'oggetto è un oggetto NSO. Qualsiasi chiamata di metodo all'oggetto genererà un avviso, ma nessuna eccezione di runtime fintanto che l'oggetto risponde effettivamente alla chiamata di metodo. L'avvertimento è ovviamente fastidioso, quindi id
è meglio. Di massima puoi quindi essere più specifico dicendo ad esempio che id<MKAnnotation>
, in questo caso, qualunque sia l'oggetto, deve essere conforme al protocollo MKAnnotation.
Se un metodo ha un tipo di id
ritorno, è possibile restituire qualsiasi oggetto Objective-C.
void
significa che il metodo non restituirà nulla.
void *
è solo un puntatore. Non sarai in grado di modificare il contenuto sull'indirizzo a cui punta il puntatore.
id
è un puntatore a un oggetto Objective-C. void *
è un puntatore a qualsiasi cosa . È possibile utilizzare void *
invece di id
, ma non è consigliabile perché non si riceveranno mai avvisi del compilatore per nulla.
Si consiglia di vedere stackoverflow.com/questions/466777/whats-the-difference-between-declaring-a-variable-id-and-nsobject e unixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs -id.html .
void *
variabili digitate sicuramente possono essere il bersaglio delle invocazioni del metodo: è un avvertimento, non un errore. Non solo, si può fare questo: int i = (int)@"Hello, string!";
e follow-up con: printf("Sending to an int: '%s'\n", [i UTF8String]);
. È un avvertimento, non un errore (e non esattamente raccomandato, né portatile). Ma il motivo per cui puoi fare queste cose è tutto di base 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;
Il codice sopra riportato proviene da objc.h, quindi sembra che id sia un'istanza di objc_object struct e che un puntatore isa possa legarsi con qualsiasi oggetto di Classe C Obiettivo, mentre void * è solo un puntatore non tipizzato.
La mia comprensione è che id rappresenta un puntatore a un oggetto mentre void * può puntare a qualsiasi cosa in realtà, purché lo si cast sul tipo in cui si desidera utilizzarlo
Oltre a quanto già detto, esiste una differenza tra oggetti e puntatori relativi alle raccolte. Ad esempio, se si desidera inserire qualcosa in NSArray, è necessario un oggetto (di tipo "id") e non è possibile utilizzare un puntatore di dati non elaborati (di tipo "void *"). È possibile utilizzare [NSValue valueWithPointer:rawData]
per convertire void *rawDdata
nel tipo "id" per utilizzarlo all'interno di una raccolta. In generale "id" è più flessibile e ha più semantica relativa agli oggetti ad esso associati. Ci sono altri esempi che spiegano il tipo id di Objective C qui .
id
si supponga che an risponda-retain
e-release
, mentre avoid*
è completamente opaco per la chiamata. Non è possibile passare un puntatore arbitrario a-performSelector:withObject:afterDelay:
(mantiene l'oggetto) e non si può presumere che+[UIView beginAnimations:context:]
manterrà il contesto (il delegato all'animazione dovrebbe mantenere la proprietà del contesto; UIKit mantiene il delegato all'animazione).