2009-08-20 7 views

Répondre

231

void * signifie « une référence à un morceau aléatoire o » la mémoire avec le contenu typées/inconnu »

id signifie « une référence à un objet Objective-C aléatoire de classe inconnue »

Il y a sémantique supplémentaire différences:

  • Sous GC uniquement ou GC modes pris en charge, le compilateur émettra des barrières d'écriture pour les références de type id, mais pas pour le type void *. Lors de la déclaration des structures, cela peut être une différence critique. Déclarer des iVars comme void *_superPrivateDoNotTouch; provoquera une récolte prématurée d'objets si _superPrivateDoNotTouch est réellement un objet. Ne fais pas ça.

  • Si vous tentez d'appeler une méthode sur une référence de type void *, un avertissement du compilateur s'affichera.

  • tenter d'invoquer une méthode sur un type id ne préviendra si la méthode appelée n'a pas été déclaré dans l'une des déclarations @interface vues par le compilateur.

Ainsi, il ne faut jamais se référer à un objet comme void *. De même, il faut éviter d'utiliser une variable typée id pour faire référence à un objet. Utilisez la référence typée de classe la plus spécifique possible. Même NSObject * est mieux que id parce que le compilateur peut, au moins, fournir une meilleure validation des invocations de méthode par rapport à cette référence.

La seule utilisation courante et valide de void * est une référence de données opaque transmise par une autre API.

Tenir compte de la méthode sortedArrayUsingFunction: context: de NSArray:

- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context; 

La fonction de tri sera déclarée comme:

NSInteger mySortFunc(id left, id right, void *context) { ...; } 

Dans ce cas, le NSArray passe simplement ce que vous passez comme argument context à la méthode à travers l'argument context. Il s'agit d'un morceau opaque de données de taille pointeur, en ce qui concerne NSArray, et vous êtes libre de l'utiliser pour le but que vous voulez.

Sans fonctionnalité de type fermeture dans la langue, c'est le seul moyen de transporter un morceau de données avec une fonction. Exemple; Si vous vouliez que mySortFunc() soit trié en fonction de la casse ou insensible à la casse, tout en restant thread-safe, vous passeriez l'indicateur sensible à la casse dans le contexte, probablement à l'entrée et à la sortie. Fragile et sujette aux erreurs, mais la seule façon de procéder est la suivante:

Les blocs résolvent ceci - Les blocs sont des fermetures pour C. Ils sont disponibles dans Clang - http://llvm.org/ et sont omniprésents dans Snow Leopard (http://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/GCD_libdispatch_Ref.pdf).

+0

De plus, je suis assez sûr qu'un 'id' est supposé répondre à '-retain' et' -release', alors qu'un 'void *' est complètement opaque à l'appelé. Vous ne pouvez pas passer un pointeur arbitraire à '-performSelector: withObject: afterDelay:' (il conserve l'objet), et vous ne pouvez pas supposer que '+ [UIView beginAnimations: context:]' conservera le contexte (le délégué d'animation devrait maintenir la propriété du contexte, UIKit conserve le délégué d'animation). –

+3

Aucune hypothèse ne peut être faite sur ce à quoi un 'id' répond. Un 'id' peut facilement se référer à une instance d'une classe qui n'est pas inhérente à' NSObject'. En pratique, cependant, votre déclaration correspond le mieux au comportement du monde réel; vous ne pouvez pas mélanger des classes d'implémentation non-'' avec l'API Foundation et aller très loin, c'est sûr! – bbum

+2

Maintenant, les types 'id' et' Class' sont traités comme [* retable object pointer *] (http://clang.llvm.org/docs/AutomaticReferenceCounting.html#objects) sous ARC. Donc, l'hypothèse est vraie au moins sous ARC. – Eonil

8

Si une méthode a un type de retour de id, vous pouvez renvoyer tout objet Objective-C.

void signifie que la méthode ne retournera rien.

void * est juste un pointeur. Vous ne serez pas en mesure d'éditer le contenu sur l'adresse vers laquelle pointe le pointeur.

+2

Comme il s'applique à la valeur de retour d'une méthode, la plupart du temps juste. Comme cela s'applique à la déclaration des variables ou des arguments, pas tout à fait. Et vous pouvez toujours transformer un (void *) en un type plus spécifique si vous voulez lire/écrire le contenu - pas que ce soit une bonne idée de le faire. – bbum

2

Ma compréhension est que id représente un pointeur vers un objet tout en void * peut pointer vers quelque chose de vraiment, aussi longtemps que vous lancez alors au type que vous voulez l'utiliser comme

+0

Si vous transtypez de (void *) vers un type d'objet, y compris id, vous risquez fort de le faire. Il y a des raisons de le faire, mais elles sont rares, éloignées et presque toujours révélatrices d'un défaut de conception. – bbum

+1

citation "il y a des raisons de le faire, mais ils sont peu, loin entre" vrai que. Ça dépend de la situation. Cependant, je ne ferais pas une déclaration générale comme "vous avez très probablement mal fait" sans un certain contexte. – hhafez

+0

Je ferais une déclaration générale; a dû traquer et réparer trop de bugs damnés à cause de couler au mauvais type avec le vide * entre les deux.La seule exception concerne les API basées sur les rappels qui prennent un argument de contexte void * dont le contrat stipule que le contexte restera inchangé entre la configuration du rappel et la réception du rappel. – bbum

19

id est un pointeur vers un object C object, où void * est un pointeur sur n'importe quoi.

id désactive également les avertissements relatifs à l'appel mthods inconnus, donc par exemple:

[(id)obj doSomethingWeirdYouveNeverHeardOf];

ne donnera pas l'avertissement habituel sur les méthodes inconnues. Il va, bien sûr, déclencher une exception lors de l'exécution, sauf si obj est nul ou vraiment implémente cette méthode.

Souvent, vous devez utiliser NSObject* ou id<NSObject> de préférence à id, dont au moins confirme que l'objet retourné est un objet de cacao, de sorte que vous pouvez utiliser en toute sécurité des méthodes telles que conserver/release/autorelease sur elle.

+2

Pour les invocations de méthode, une cible de type (id) génère un avertissement si la méthode ciblée n'a été déclarée nulle part. Ainsi, dans votre exemple, doSomethingWeirdYouveNeverHeardOf aurait dû être déclaré quelque part pour qu'il n'y ait pas d'avertissement. – bbum

+0

Vous avez raison, un meilleur exemple serait quelque chose comme storagePolicy. –

+0

@PeterNLewis Je ne suis pas d'accord sur 'Souvent vous devriez utiliser NSObject *' au lieu de 'id'. En spécifiant 'NSObject *', vous dites explicitement que l'objet est un objet NSObject. Tout appel de méthode à l'objet entraînera un avertissement, mais aucune exception d'exécution tant que cet objet répond à l'appel de la méthode. L'avertissement est évidemment ennuyeux, donc «id» est mieux. De grossier vous pouvez alors être plus spécifique en disant par exemple 'id ', qui dans ce cas signifie quel que soit l'objet, il doit se conformer au protocole MKAnnotation. – pnizzle

7

id est un pointeur vers un objet Objective-C. void * est un pointeur sur quoi que ce soit. Vous pouvez utiliser void * au lieu de id, mais ce n'est pas recommandé car vous n'obtiendrez jamais d'avertissements de compilateur pour quoi que ce soit. Vous pouvez voir stackoverflow.com/questions/466777/whats-the-difference-between-declaring-a-variable-id-and-nsobject et unixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs-id.html.

+1

Pas tout à fait. Les variables typées (void *) ne peuvent pas être la cible des invocations de méthodes. Il en résulte "avertissement: type de destinataire invalide 'void *'" du compilateur. – bbum

+0

@bbum: les variables typées 'void *' peuvent très certainement être la cible d'invocations de méthodes - c'est un avertissement, pas une erreur. Non seulement cela, vous pouvez le faire: 'int i = (int) @" Bonjour, chaîne! ";' Et suivi avec: 'printf (" Envoi à un int: '% s' \ n ", [i UTF8String ]); C'est un avertissement, pas une erreur (et pas exactement recommandé, ni portable). Mais la raison pour laquelle vous pouvez faire ces choses est tout basique C. – johne

+0

Désolé. Vous avez raison; c'est un avertissement, pas une erreur. Je traite les avertissements comme des erreurs toujours et partout. – bbum

4
/// 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; 

Le code ci-dessus est de objc.h, donc ressemble id est une instance de pointeur struct et isa objc_object peut se lier avec un objet Objectif de classe C, alors que void * est juste un pointeur non typé.

0

En plus de ce qui a déjà été dit, il existe une différence entre les objets et les pointeurs liés aux collections. Par exemple, si vous voulez mettre quelque chose dans NSArray, vous avez besoin d'un objet (de type "id"), et vous ne pouvez pas utiliser un pointeur de données brutes (de type "void *"). Vous pouvez utiliser [NSValue valueWithPointer:rawData] pour convertir void *rawDdata au type "id" pour l'utiliser dans une collection. En général, "id" est plus flexible et a plus de sémantique liée aux objets qui y sont attachés. Il y a plus d'exemples expliquant id type of Objective C here.

Questions connexes