Question

Clang ajoute un mot-clé instancetype que, aussi loin que je peux voir, remplace id un type de retour dans -alloc et init.

Est-il un avantage à l'utilisation de instancetype au lieu de id?

Était-ce utile?

La solution

Il ya certainement un avantage.Lorsque vous utilisez 'id', vous obtenez essentiellement aucune vérification de type à tous.Avec type d'instance, le compilateur et l'IDE de savoir ce genre de chose est retourné, et peut vérifier votre code de la meilleure et la saisie semi-automatique de mieux.

Utiliser uniquement dans un endroit où il fait sens, bien sûr (c'est à direune méthode qui retourne une instance de cette classe);id est toujours utile.

Autres conseils

Oui, il y a des avantages à utiliser instancetype dans tous les cas où elle s'applique.Je vais vous expliquer plus en détail, mais permettez-moi de commencer avec cette déclaration audacieuse:Utilisation instancetype chaque fois que c'est nécessaire, ce qui est à chaque fois une classe renvoie une instance de la même classe.

En fait, voici ce que Apple dit maintenant sur le sujet:

Dans votre code, remplace les occurrences de id une valeur de retour avec instancetype le cas échéant.C'est généralement le cas pour les init les méthodes de classe et de méthodes de fabrique.Même si le compilateur convertit automatiquement les méthodes qui commencent par “alloc,” “init” ou “nouveau” et avoir un type de retour id de retour instancetype, il ne convertit pas les autres méthodes. Objective-C de la convention est d'écrire instancetype explicitement, pour toutes les méthodes.

Avec cela de la route, passons à autre chose et d'expliquer pourquoi c'est une bonne idée.

Tout d'abord, quelques définitions:

 @interface Foo:NSObject
 - (id)initWithBar:(NSInteger)bar; // initializer
 + (id)fooWithBar:(NSInteger)bar;  // class factory
 @end

Pour une fabrique de classe, vous devez toujours utilisation instancetype.Le compilateur ne convertit pas automatiquement id pour instancetype.Que id est un objet générique.Mais si vous faites un instancetype le compilateur sait quel type d'objet, la méthode retourne.

C'est pas un universitaire problème.Par exemple, [[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData] va générer une erreur sur Mac OS X (seulement) Plusieurs méthodes nommé "writeData:" trouvés avec dépareillés conséquent, le type de paramètre ou les attributs.La raison en est que les deux NSFileHandle et NSURLHandle fournir un writeData:.Depuis [NSFileHandle fileHandleWithStandardOutput] retourne un id, le compilateur n'est pas certain de classe writeData: l'a appelée.

Vous avez besoin de travailler autour de cela, en utilisant soit:

[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];

ou:

NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];

Bien sûr, la meilleure solution est de déclarer fileHandleWithStandardOutput comme le retour d'un instancetype.Ensuite, la distribution ou à la cession n'est pas nécessaire.

(À noter que sur iOS, cet exemple ne produira pas une erreur que seulement NSFileHandle fournit un writeData: il n'.D'autres exemples existent, comme length, qui retourne un CGFloat à partir de UILayoutSupport mais un NSUInteger à partir de NSString.)

Note:Depuis que j'ai écrit cela, macOS sur les en-têtes ont été modifiés pour retourner un NSFileHandle au lieu d'une id.

Pour les initialiseurs, c'est plus compliqué.Lorsque vous tapez ceci:

- (id)initWithBar:(NSInteger)bar

...le compilateur fera semblant que vous avez tapé ceci à la place:

- (instancetype)initWithBar:(NSInteger)bar

Cela était nécessaire pour l'ARC.Ceci est décrit dans Clang Extensions de Langage Liés à des types de résultats.C'est pourquoi les gens vous diront qu'il n'est pas nécessaire d'utiliser instancetype, bien que je pense que vous devriez.Le reste de cette réponse traite de cette.

Il y a trois avantages:

  1. Explicite. Votre code est en train de faire ce qu'il dit, plutôt que quelque chose d'autre.
  2. De modèle. Vous êtes en train de construire de bonnes habitudes pour une fois il n'est question, qui n'existe pas.
  3. La cohérence. Vous avez établi une certaine cohérence dans votre code, ce qui le rend plus lisible.

Explicite

C'est vrai qu'il n'y a pas de technique avantage pour le retour instancetype à partir d'un init.Mais c'est parce que le compilateur convertit automatiquement le id pour instancetype.Vous êtes en s'appuyant sur cette bizarrerie;lorsque vous écrivez que la init retourne un id, le compilateur est l'interprétant comme si elle renvoie un instancetype.

Ce sont équivalent pour le compilateur:

- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;

Ce ne sont pas équivalent à vos yeux.Au mieux, vous apprendrez à ignorer la différence et le lait écrémé sur elle. Ce n'est pas quelque chose que vous devriez apprendre à ignorer.

Modèle

Alors il n'y a pas de différence avec init et d'autres méthodes, il y est une différence dès que vous définissez une classe d'usine.

Ces deux ne sont pas équivalents:

+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;

Vous voulez la deuxième forme.Si vous avez l'habitude de taper instancetype comme le type de retour d'un constructeur, vous aurez droit à chaque fois.

La cohérence

Enfin, imaginez si vous mettez tout cela ensemble:vous voulez un init la fonction et aussi une fabrique de classe.

Si vous utilisez id pour init, vous vous retrouvez avec un code comme ceci:

- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;

Mais si vous l'utilisez instancetype, vous obtenez ceci:

- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;

C'est plus cohérent et plus lisible.Ils renvoient la même chose, et maintenant, c'est évident.

Conclusion

Sauf si vous êtes intentionnellement à l'écriture de code pour les anciens compilateurs, vous devez utiliser instancetype lorsque cela est approprié.

Vous devriez hésité avant d'écrire un message qui renvoie id.Demandez-vous:Est-ce le retour d'une instance de cette classe?Si oui, c'est un instancetype.

Il y a certainement des cas où vous devez retourner id, mais vous allez probablement utiliser instancetype beaucoup plus fréquemment.

Réponses ci-dessus sont plus que suffisant pour expliquer cette question.Je voudrais juste ajouter un exemple pour les lecteurs à comprendre en termes de codage.

ClassA

@interface ClassA : NSObject

- (id)methodA;
- (instancetype)methodB;

@end

Classe B

@interface ClassB : NSObject

- (id)methodX;

@end

TestViewController.m

#import "ClassA.h"
#import "ClassB.h"

- (void)viewDidLoad {

    [[[[ClassA alloc] init] methodA] methodX]; //This will NOT generate a compiler warning or error because the return type for methodA is id. Eventually this will generate exception at runtime

    [[[[ClassA alloc] init] methodB] methodX]; //This will generate a compiler error saying "No visible @interface ClassA declares selector methodX" because the methodB returns instanceType i.e. the type of the receiver
}

Vous pouvez également obtenir des détails à Désignés Initialiseur

**

INSTANCETYPE

** Ce mot clé peut uniquement être utilisé pour le type de retour, qu'il corresponde avec le type de retour de récepteur.méthode init toujours déclarée pour retourner instancetype.Pourquoi ne pas faire le type de retour de Partie de partie de, par exemple?Que serait la cause d'un problème si le Parti de classe n'a jamais été sous-classé.La sous-classe héritera de toutes les méthodes de la Partie, y compris de l'initialiseur et son type de retour.Si une instance de la sous-classe a été envoyé cette initialiseur de message, ce serait de retour?Pas un pointeur vers une Partie de l'instance, mais un pointeur vers une instance de la sous-classe.Vous pourriez penser que c'est Pas un problème, je vais remplacer l'initialiseur de la sous-classe pour changer le type de retour.Mais en Objective-C, vous ne pouvez pas avoir les deux méthodes avec le même sélecteur et les différents types de retour (ou arguments).En spécifiant que la méthode d'initialisation de retour "une instance de l'objet qui reçoit," vous ne l'avez jamais à vous inquiéter de ce qui se passe dans cette situation.**

ID

** Avant que le type d'instance a été introduite en Objective-C, initialiseurs de retour d'une id (eye-dee).Ce type est défini comme "un pointeur vers un objet".(id est un peu comme de void * en C) de cette écriture, XCode modèles de classe toujours utiliser l'id que le type de retour d'initialiseurs ajouté dans le code réutilisable.À la différence de type d'instance, id peut être utilisé comme plus que juste un type de retour.Vous pouvez déclarer des variables ou des paramètres de la méthode de type id lorsque vous n'êtes pas sûr quel type d'objet, la variable sera à la fin pointe.Vous pouvez utiliser l'id lors de l'utilisation rapide énumération d'itérer sur un tableau de plusieurs ou unknow types d'objets.Notez que parce que le code n'est pas défini comme "un pointeur vers un objet, d'un" vous ne comprennent pas * lors de la déclaration d'une variable ou d'un paramètre d'objet de ce type.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top