Réponses:
void *
signifie "une référence à un morceau aléatoire de mémoire avec un contenu non typé / inconnu"
id
signifie "une référence à un objet Objective-C aléatoire de classe inconnue"
Il existe des différences sémantiques supplémentaires:
Sous les modes GC Only ou GC Supported, 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 iVars comme void *_superPrivateDoNotTouch;
provoquera une récolte prématurée d'objets s'il _superPrivateDoNotTouch
s'agit en fait d'un objet. Ne fais pas ça.
tenter d'invoquer une méthode sur une référence de void *
type provoquera un avertissement du compilateur.
tenter d'invoquer une méthode sur un id
type avertira uniquement si la méthode appelée n'a été déclarée dans aucune des @interface
déclarations vues par le compilateur.
Ainsi, il ne faut jamais se référer à un objet comme un void *
. De même, il faut éviter d'utiliser une id
variable typée pour faire référence à un objet. Utilisez la référence typée de classe la plus spécifique possible. Même NSObject *
c'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 qui est transmise via une autre API.
Considérez la sortedArrayUsingFunction: context:
méthode de NSArray
:
- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context;
La fonction de tri serait déclarée comme:
NSInteger mySortFunc(id left, id right, void *context) { ...; }
Dans ce cas, le NSArray transmet simplement ce que vous passez en tant context
qu'argument à la méthode en tant context
qu'argument. Il s'agit d'un morceau opaque de données de la taille d'un pointeur, en ce qui concerne NSArray, et vous êtes libre de l'utiliser à toutes fins utiles.
Sans une fonctionnalité de type de fermeture dans le langage, c'est le seul moyen de transporter un morceau de données avec une fonction. Exemple; si vous vouliez que mySortFunc () trie conditionnellement comme sensible à la casse ou insensible à la casse, tout en étant toujours thread-safe, vous passeriez l'indicateur is-case-sensitive dans le contexte, probablement en cours d'entrée et de sortie.
Fragile et sujet aux erreurs, mais le seul moyen.
Les blocs résolvent ce problème - 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 ).
id
répond. An id
peut facilement faire référence à 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 <NSObject>
classes non implémentées avec l'API Foundation et aller très loin, c'est certainement sûr!
id
, les Class
types sont traités comme un pointeur d'objet conservable sous ARC. Donc, l'hypothèse est vraie au moins sous ARC.
id est un pointeur vers un objet objectif C, où as void * est un pointeur vers n'importe quoi.
id désactive également les avertissements liés à l'appel de mthods inconnus, par exemple:
[(id)obj doSomethingWeirdYouveNeverHeardOf];
ne donnera pas l'avertissement habituel sur les méthodes inconnues. Il lèvera, bien sûr, une exception au moment de l'exécution à moins que obj ne soit nul ou n'implémente vraiment cette méthode.
Souvent, vous devriez utiliser NSObject*
ou id<NSObject>
de préférence id
, ce qui confirme au moins que l'objet retourné est un objet Cocoa, afin que vous puissiez utiliser en toute sécurité des méthodes telles que retenir / relâcher / autorelease dessus.
Often you should use NSObject*
lieu de id
. En spécifiant, NSObject*
vous dites en fait explicitement que l'objet est un NSObject. Tout appel de méthode à l'objet entraînera un avertissement, mais aucune exception d'exécution tant que cet objet répondra effectivement à l'appel de méthode. L'avertissement est évidemment ennuyeux, id
c'est donc mieux. De grossier vous pouvez alors être plus précis en disant par exemple id<MKAnnotation>
, ce qui dans ce cas signifie quel que soit l'objet, il doit se conformer au protocole MKAnnotation.
Si une méthode a un type de id
retour, vous pouvez renvoyer n'importe quel objet Objective-C.
void
signifie que la méthode ne retournera rien.
void *
est juste un pointeur. Vous ne pourrez pas modifier le contenu de l'adresse vers laquelle pointe le pointeur.
id
est un pointeur vers un objet Objective-C. void *
est un pointeur vers n'importe quoi . Vous pouvez utiliser à la void *
place de id
, mais ce n'est pas recommandé car vous n'obtiendrez jamais d'avertissement du compilateur pour quoi que ce soit.
Vous voudrez peut-être voir stackoverflow.com/questions/466777/whats-the-difference-b between - declaring-a - variable - id- and- nsobject et unixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs -id.html .
void *
les variables typées peuvent très certainement être la cible des invocations de méthodes - c'est un avertissement, pas une erreur. Non seulement cela, vous pouvez le faire: int i = (int)@"Hello, string!";
et le suivi avec: printf("Sending to an 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 de 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;
Le code ci-dessus provient de objc.h, il semble donc que id est une instance de objc_object struct et isa pointer peut se lier à n'importe quel objet Objective C Class, tandis que void * est juste un pointeur non typé.
Je crois comprendre que id représente un pointeur vers un objet tandis que void * peut vraiment pointer vers n'importe quoi, tant que vous le castez ensuite vers le type que vous souhaitez utiliser comme
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 y utiliser de 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 lui sont attachés. Il y a plus d'exemples expliquant le type d'identifiant de l'Objectif C ici .
id
est supposé répondre à-retain
et-release
, alors que avoid*
est complètement opaque pour 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:]
cela conservera le contexte (le délégué d'animation doit conserver la propriété du contexte; UIKit conserve le délégué d'animation).