Clang ajoute un mot clé instancetype
qui, autant que je sache, remplace id
comme type de retour dans -alloc
et init
.
Y a-t-il un avantage à utiliser instancetype
au lieu de id
?
objective-c
instancetype
griotspeak
la source
la source
id
étaient utilisées pour revenir ont été remplacéesinstancetype
, même àinit
partir deNSObject
. Si vous voulez rendre votre code compatible avec swift, vous devez utiliserinstancetype
Réponses:
Il y a certainement un avantage. Lorsque vous utilisez 'id', vous n'obtenez pratiquement aucune vérification de type. Avec instancetype, le compilateur et l'EDI savent quel type de chose est retourné et peuvent mieux vérifier votre code et la saisie semi-automatique.
Utilisez-le uniquement là où cela a du sens bien sûr (c'est-à-dire une méthode qui renvoie une instance de cette classe); id est toujours utile.
la source
alloc
,init
Etc. sont automatiquement promuinstancetype
par le compilateur. Cela ne veut pas dire qu'il n'y a aucun avantage; il y en a, mais ce n'est pas ça.Oui, il y a des avantages à utiliser
instancetype
dans tous les cas où cela s'applique. J'expliquerai plus en détail, mais permettez-moi de commencer par cette déclaration en gras: utilisezinstancetype
chaque fois que cela est approprié, c'est-à-dire chaque fois qu'une classe renvoie une instance de cette même classe.En fait, voici ce qu'Apple dit maintenant sur le sujet:
Avec cela à l'écart, passons à autre chose et expliquons pourquoi c'est une bonne idée.
Tout d'abord, quelques définitions:
Pour une usine de classe, vous devez toujours utiliser
instancetype
. Le compilateur ne se convertit pas automatiquementid
eninstancetype
. C'estid
un objet générique. Mais si vous en faites un,instancetype
le compilateur sait quel type d'objet la méthode retourne.Ce n'est pas un problème académique. Par exemple,
[[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData]
générera une erreur sur Mac OS X ( uniquement ) Plusieurs méthodes nommées «writeData:» trouvées avec un résultat, un type de paramètre ou des attributs incompatibles . La raison en est que NSFileHandle et NSURLHandle fournissent awriteData:
. Depuis[NSFileHandle fileHandleWithStandardOutput]
retourne unid
, le compilateur n'est pas certain de quelle classewriteData:
est appelée.Vous devez contourner cela en utilisant soit:
ou:
Bien sûr, la meilleure solution est de déclarer
fileHandleWithStandardOutput
comme retournant uninstancetype
. Ensuite, le casting ou l'affectation n'est pas nécessaire.(Notez que sur iOS, cet exemple ne produira pas d'erreur car il n'en
NSFileHandle
fournit qu'unwriteData:
. D'autres exemples existent, tels quelength
, qui renvoie unCGFloat
deUILayoutSupport
mais unNSUInteger
deNSString
.)Remarque : depuis que j'ai écrit ceci, les en-têtes macOS ont été modifiés pour renvoyer un
NSFileHandle
au lieu d'unid
.Pour les initialiseurs, c'est plus compliqué. Lorsque vous tapez ceci:
… Le compilateur prétendra que vous avez tapé ceci à la place:
C'était nécessaire pour l'ARC. Ceci est décrit dans les types de résultats Clang Language Extensions Related . C'est pourquoi les gens vous diront qu'il n'est pas nécessaire d'utiliser
instancetype
, bien que je soutienne que vous devriez. Le reste de cette réponse traite de cela.Il y a trois avantages:
Explicite
C'est vrai qu'il n'y a aucun avantage technique à revenir
instancetype
d'uninit
. Mais c'est parce que le compilateur convertit automatiquement le fichierid
eninstancetype
. Vous comptez sur cette bizarrerie; pendant que vous écrivez que leinit
renvoie unid
, le compilateur l'interprète comme s'il renvoyait uninstancetype
.Ceux-ci sont équivalents au compilateur:
Ce ne sont pas équivalents à vos yeux. Au mieux, vous apprendrez à ignorer la différence et à la survoler. Ce n'est pas quelque chose que vous devez apprendre à ignorer.
Modèle
Bien qu'il n'y ait pas de différence avec les
init
autres méthodes, il y a une différence dès que vous définissez une fabrique de classe.Ces deux ne sont pas équivalents:
Vous voulez le deuxième formulaire. Si vous avez l'habitude de taper
instancetype
comme type de retour d'un constructeur, vous aurez toujours raison.Cohérence
Enfin, imaginez si vous mettez tout cela ensemble: vous voulez une
init
fonction et aussi une fabrique de classe.Si vous utilisez
id
pourinit
, vous vous retrouvez avec un code comme celui-ci:Mais si vous utilisez
instancetype
, vous obtenez ceci:C'est plus cohérent et plus lisible. Ils retournent la même chose, et maintenant c'est évident.
Conclusion
À moins que vous n'écriviez intentionnellement du code pour d'anciens compilateurs, vous devez l'utiliser
instancetype
lorsque cela est approprié.Vous devriez hésiter avant d'écrire un message qui revient
id
. Posez-vous la question: cela renvoie-t-il une instance de cette classe? Si oui, c'est uninstancetype
.Il y a certainement des cas où vous devez revenir
id
, mais vous en utiliserez probablementinstancetype
beaucoup plus fréquemment.la source
instancetype
vsid
n'est vraiment pas une décision de style. Les récents changements autour deinstancetype
nousinstancetype
-init
Les réponses ci-dessus sont plus que suffisantes pour expliquer cette question. Je voudrais juste ajouter un exemple pour que les lecteurs le comprennent en termes de codage.
Classe A
Classe B
TestViewController.m
la source
Vous pouvez également obtenir des détails dans l' initialiseur désigné
**
INSTANCETYPE
** Ce mot-clé ne peut être utilisé que pour le type de retour, qu'il correspond au type de retour du récepteur. La méthode init a toujours déclaré renvoyer instancetype. Pourquoi ne pas faire du type de retour Party par exemple Party, par exemple? Cela causerait un problème si la classe Party était jamais sous-classée. La sous-classe hériterait de toutes les méthodes de Party, y compris l'initialiseur et son type de retour. Si une instance de la sous-classe recevait ce message d'initialisation, ce serait return? Pas un pointeur vers une instance de Party, mais un pointeur vers une instance de sous-classe. Vous pourriez penser que ce n'est pas un problème, je remplacerai l'initialiseur dans la sous-classe pour changer le type de retour. Mais dans Objective-C, vous ne pouvez pas avoir deux méthodes avec le même sélecteur et différents types de retour (ou arguments). En spécifiant qu'une méthode d'initialisation renvoie "
Identifiant
** Avant l'introduction du type d'instance dans Objective-C, les initialiseurs renvoient id (eye-dee). Ce type est défini comme "un pointeur vers n'importe quel objet". (id ressemble beaucoup à void * en C.) À ce jour, les modèles de classe XCode utilisent toujours id comme type de retour des initialiseurs ajoutés dans le code passe-partout. Contrairement à instancetype, id peut être utilisé comme plus qu'un simple type de retour. Vous pouvez déclarer des variables ou des paramètres de méthode de type id lorsque vous ne savez pas quel type d'objet la variable finira par pointer. Vous pouvez utiliser id lorsque vous utilisez l'énumération rapide pour itérer sur un tableau de types d'objets multiples ou inconnus. Notez que comme id n'est pas défini comme "un pointeur vers n'importe quel objet", vous n'incluez pas de * lorsque vous déclarez une variable ou un paramètre d'objet de ce type.
la source