Quelle est la différence entre id
et void *
?Objective-C: différence entre id et void *
Répondre
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 typevoid *
. Lors de la déclaration des structures, cela peut être une différence critique. Déclarer des iVars commevoid *_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).
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.
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
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
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
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
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
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.
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
Vous avez raison, un meilleur exemple serait quelque chose comme storagePolicy. –
@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
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.
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
@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
Désolé. Vous avez raison; c'est un avertissement, pas une erreur. Je traite les avertissements comme des erreurs toujours et partout. – bbum
/// 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é.
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.
- 1. Différence entre Delegate.Invoke et délégué()
- 2. Y at-il une différence entre foo (void) et foo() en C++ ou C?
- 3. Différence entre -Wconversion entre gcc et g ++
- 4. Différence entre sqlreader et
- 5. Différence entre DECLARE_DYNAMIC et DECLARE_DYNCREATE?
- 6. Différence entre JNLP et JavaFX
- 7. Différence entre Session et HttpContext.Current.Session
- 8. Différence entre HTTPContextBase.User et MembershipUser
- 9. Différence entre FileStreamResult et FilePathResult?
- 10. Différence entre scanf() et fgets()
- 11. Différence entre DataGrid et GridView
- 12. Différence entre "__method__" et "méthode"
- 13. Différence entre BOOST_CHECK_CLOSE et BOOST_CHECK_CLOSE_FRACTION?
- 14. Différence entre "\ n" et Environment.NewLine
- 15. Différence entre admin.site.root et admin.site.urls
- 16. Différence entre Roles.GetRolesForUser et Roles.Provider.GetRolesForUser?
- 17. Différence entre val() et text()
- 18. Différence entre control.Attributes.Add et control.Attributes []
- 19. Différence entre value et itemvalue
- 20. Différence entre Send.nc et SendMsg.nc
- 21. Différence entre System.Web.Cache et HTTPContext.Curent.Cache
- 22. Différence entre feature et plugin.xml?
- 23. Différence entre verrou (verrou) et verrouillage (variable_qui_utilise_I_am_using)
- 24. Coulée entre void * et un pointeur vers la fonction membre
- 25. Différence entre un WebControl et un CompositeControl?
- 26. Existe-t-il une différence entre id, date ou date, id index dans SQL Server
- 27. Quelle est la différence entre déclarer une variable "id" et "NSObject *"?
- 28. Différence entre l'exception d'application et l'exception système
- 29. Différence entre iCalendar (.ics) et vCalendar (.vcs)
- 30. Différence entre un objet et un hachage?
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). –
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
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