Antworten:
void *
bedeutet "ein Verweis auf einen zufälligen Speicherblock mit untypisiertem / unbekanntem Inhalt"
id
bedeutet "eine Referenz auf ein zufälliges Objective-C-Objekt unbekannter Klasse"
Es gibt zusätzliche semantische Unterschiede:
Im Modus "Nur GC" oder "GC-unterstützt" gibt der Compiler Schreibbarrieren für Typreferenzen aus id
, jedoch nicht für Typreferenzen void *
. Bei der Deklaration von Strukturen kann dies ein kritischer Unterschied sein. Das Deklarieren von iVars like void *_superPrivateDoNotTouch;
führt zu vorzeitigem Ernten von Objekten, wenn _superPrivateDoNotTouch
es sich tatsächlich um ein Objekt handelt. Tu das nicht.
Wenn Sie versuchen, eine Methode für eine Referenz des void *
Typs aufzurufen, wird eine Compiler-Warnung angezeigt.
Der Versuch, eine Methode für einen id
Typ aufzurufen, warnt nur, wenn die aufgerufene Methode in keiner der @interface
vom Compiler angezeigten Deklarationen deklariert wurde .
Daher sollte man ein Objekt niemals als ein Objekt bezeichnen void *
. Ebenso sollte vermieden werden, eine id
typisierte Variable zu verwenden, um auf ein Objekt zu verweisen. Verwenden Sie die spezifischste klassentypisierte Referenz, die Sie können. Auch NSObject *
ist besser alsid
weil der Compiler zumindest eine bessere Validierung von Methodenaufrufen anhand dieser Referenz bereitstellen kann.
Die eine übliche und gültige Verwendung von void *
ist eine undurchsichtige Datenreferenz, die über eine andere API übertragen wird.
Betrachten Sie die sortedArrayUsingFunction: context:
Methode von NSArray
:
- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context;
Die Sortierfunktion würde wie folgt deklariert:
NSInteger mySortFunc(id left, id right, void *context) { ...; }
In diesem Fall übergibt der NSArray lediglich alles, was Sie als context
Argument an die Methode übergeben, alscontext
Argument. Für NSArray handelt es sich um einen undurchsichtigen Teil von Daten in Zeigergröße, und Sie können sie für jeden gewünschten Zweck verwenden.
Ohne ein Abschluss-Feature in der Sprache ist dies die einzige Möglichkeit, einen Datenblock mit einer Funktion mitzunehmen. Beispiel; Wenn Sie möchten, dass mySortFunc () bedingt nach Groß- und Kleinschreibung oder ohne Berücksichtigung der Groß- und Kleinschreibung sortiert, während Sie dennoch threadsicher sind, würden Sie den Indikator, bei dem zwischen Groß- und Kleinschreibung unterschieden wird, im Kontext übergeben und wahrscheinlich auf dem Weg nach innen und nach außen wirken.
Zerbrechlich und fehleranfällig, aber der einzige Weg.
Blöcke lösen dieses Problem - Blöcke sind Verschlüsse für C. Sie sind in Clang - http://llvm.org/ verfügbar und in Snow Leopard ( http://developer.apple.com/library/ios/documentation/Performance) allgegenwärtig /Reference/GCD_libdispatch_Ref/GCD_libdispatch_Ref.pdf ).
id
reagiert wird. Ein id
kann leicht auf eine Instanz einer Klasse verweisen, die nicht inhärent ist NSObject
. In der Praxis passt Ihre Aussage jedoch am besten zum Verhalten in der realen Welt. Sie können nicht <NSObject>
implementierende Klassen nicht mit der Foundation API mischen und kommen sehr weit, das ist definitiv sicher!
id
und Class
Typen werden unter ARC als Zeiger für beibehaltene Objekte behandelt . Die Annahme trifft also zumindest unter ARC zu.
id ist ein Zeiger auf ein objektives C-Objekt, wobei void * ein Zeiger auf irgendetwas ist.
id deaktiviert auch Warnungen im Zusammenhang mit dem Aufrufen unbekannter Methoden, z. B.:
[(id)obj doSomethingWeirdYouveNeverHeardOf];
gibt nicht die übliche Warnung vor unbekannten Methoden. Es wird natürlich zur Laufzeit eine Ausnahme auslösen, es sei denn, obj ist null oder implementiert diese Methode tatsächlich.
Oft sollten Sie verwenden NSObject*
oder id<NSObject>
bevorzugen id
, was zumindest bestätigt, dass das zurückgegebene Objekt ein Kakao-Objekt ist, sodass Sie sicher Methoden wie Beibehalten / Freigeben / Autorelease verwenden können.
Often you should use NSObject*
statt id
. Mit der Angabe NSObject*
sagen Sie tatsächlich explizit, dass das Objekt ein NSObject ist. Jeder Methodenaufruf an das Objekt führt zu einer Warnung, jedoch zu keiner Laufzeitausnahme, solange dieses Objekt tatsächlich auf den Methodenaufruf reagiert. Die Warnung ist offensichtlich ärgerlich, also id
ist es besser. Grob gesagt können Sie dann genauer sagen id<MKAnnotation>
, indem Sie beispielsweise sagen , was in diesem Fall bedeutet, dass das Objekt, unabhängig vom Objekt, dem MKAnnotation-Protokoll entsprechen muss.
Wenn eine Methode einen Rückgabetyp von id
hat, können Sie jedes Objective-C-Objekt zurückgeben.
void
bedeutet, dass die Methode nichts zurückgibt.
void *
ist nur ein Zeiger. Sie können den Inhalt der Adresse, auf die der Zeiger zeigt, nicht bearbeiten.
id
ist ein Zeiger auf ein Objective-C-Objekt. void *
ist ein Zeiger auf alles . Sie könnten void *
anstelle von verwendenid
, aber es wird nicht empfohlen, da Sie niemals Compiler-Warnungen für irgendetwas erhalten würden.
Möglicherweise möchten Sie stackoverflow.com/questions/466777/whats-the-difference-bet-clare-a-variable-id-and-nsobject und unixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs anzeigen -id.html .
void *
Variablen können definitiv das Ziel von Methodenaufrufen sein - es ist eine Warnung, kein Fehler. Nicht nur das, Sie können dies tun: int i = (int)@"Hello, string!";
und folgen Sie mit : printf("Sending to an int: '%s'\n", [i UTF8String]);
. Es ist eine Warnung, kein Fehler (und nicht genau empfohlen oder tragbar). Aber der Grund, warum Sie diese Dinge tun können, ist alles grundlegende 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;
Der obige Code stammt von objc.h, es sieht also so aus, als ob id eine Instanz der Struktur objc_object ist und ein Zeiger an jedes Objekt der Objective C-Klasse gebunden werden kann, während void * nur ein untypisierter Zeiger ist.
Mein Verständnis ist, dass id einen Zeiger auf ein Objekt darstellt, während void * wirklich auf alles verweisen kann, solange Sie es dann in den Typ umwandeln, als den Sie es verwenden möchten
Zusätzlich zu dem, was bereits gesagt wurde, gibt es einen Unterschied zwischen Objekten und Zeigern, die sich auf Sammlungen beziehen. Wenn Sie beispielsweise etwas in NSArray einfügen möchten, benötigen Sie ein Objekt (vom Typ "id") und können dort keinen Rohdatenzeiger (vom Typ "void *") verwenden. Sie können den Typ "id" [NSValue valueWithPointer:rawData]
konvertieren void *rawDdata
, um ihn in einer Sammlung zu verwenden. Im Allgemeinen ist "id" flexibler und hat mehr Semantik in Bezug auf Objekte, die damit verbunden sind. Weitere Beispiele zur Erläuterung des ID-Typs von Ziel C finden Sie hier .
id
angenommen wird, dass a auf-retain
und reagiert-release
, während avoid*
für den Angerufenen völlig undurchsichtig ist. Sie können keinen beliebigen Zeiger an übergeben-performSelector:withObject:afterDelay:
(das Objekt bleibt erhalten), und Sie können nicht davon ausgehen, dass+[UIView beginAnimations:context:]
der Kontext erhalten bleibt (der Animationsdelegierte sollte das Eigentum am Kontext behalten; UIKit behält den Animationsdelegierten).