Pourquoi utiliser (id) dans une signature de méthode quand (NSObject *) serait plus précis?

StackOverflow https://stackoverflow.com/questions/1829028

  •  11-09-2019
  •  | 
  •  

Question

Chaque fois que je mets en œuvre une méthode dans mon propre code qui peut accepter ou retourner des objets de plus d'une classe, j'essaie toujours d'utiliser la superclasse plus spécifique disponible. Par exemple, si je devais mettre en œuvre une méthode qui pourrait renvoyer un NSArray * ou un NSDictionary * en fonction de son entrée, je donnerais cette méthode un type de retour de NSObject *, puisque c'est superclasse commune la plus directe. Voici un exemple:

@interface MyParser()
- (BOOL)stringExpressesKeyValuePairs:(NSString *)string;
- (BOOL)stringExpressesAListOfEntities:(NSString *)string;
- (NSArray *)parseArrayFromString:(NSString *)string;
- (NSDictionary *)parseDictionaryFromString:(NSString *)string;
@end

@implementation MyParser
- (NSObject *)parseString:(NSString *)string {
    if ([self stringExpressesKeyValuePairs:string]) {
        return [self parseDictionaryFromString:string];
    }
    else if ([self stringExpressesAListOfEntities:string]) {
        return [self parseArrayFromString:string];
    }
}
// etc...
@end

Je l'ai remarqué de nombreux cas dans Foundation et d'autres API où Apple utilise (id) dans certaines signatures de méthode quand (NSObject *) serait plus précis. Par exemple, voici une méthode de NSPropertyListSerialization:

+ (id)propertyListFromData:(NSData *)data 
          mutabilityOption:(NSPropertyListMutabilityOptions)opt 
                    format:(NSPropertyListFormat *)format 
          errorDescription:(NSString **)errorString

Les types de retour possibles de cette méthode sont NSData, NSString, NSArray, NSDictionary, NSDate et NSNumber. Il me semble qu'un type de retour (NSObject *) serait un meilleur choix que (id), puisque l'appelant serait alors en mesure d'appeler des méthodes comme NSObject conserver sans type cast.

J'essaie généralement d'imiter les idiomes établis par les cadres officiels, mais j'aime aussi comprendre ce qui les motive. Je suis sûr que Apple a une raison valable pour l'utilisation (id) dans des cas comme celui-ci, mais je ne suis pas le voir. Qu'est-ce que je manque?

Était-ce utile?

La solution

Utilisation id indique au compilateur, il sera un objet de type inconnu. En utilisant NSObject le compilateur alors vous attendre à être seulement en utilisant des messages disponibles à NSObject. Alors ... Si vous connaissez un tableau a été retourné et il est casté comme id, vous pouvez appeler objectAtIndex: sans avertissement du compilateur. Alors que le retour avec un casting de NSObject, vous obtiendrez des avertissements.

Autres conseils

La raison pour laquelle (id) est utilisé dans les déclarations de méthode est double:

(1) Le procédé peut prendre ou remettre tout type. NSArray contient un objet aléatoire et, par conséquent, objectAtIndex: retourne un objet de tout type aléatoire. Coulée à NSObject* ou id <NSObject> serait erroné pour deux raisons; Tout d'abord, un tableau peut contenir des sous-classes non NSObject aussi longtemps qu'ils mettent en œuvre un certain petit ensemble de méthodes et, d'autre part, un type de retour spécifique nécessiteraient la coulée.

(2) Objective-C ne supporte pas les déclarations covariantes. Considérez:

@interface NSArray:NSObject
+ (id) array;
@end

Maintenant, vous pouvez appeler +array sur les deux NSArray et NSMutableArray. L'ancien retourne un tableau immuable et celle-ci un réseau mutable. En raison du manque de Objective-C de l'appui de la déclaration covariant, si ce qui précède ont été déclarés comme retour (NSArray*), les clients de la méthode des sous-classes devraient jeter à `(NSMutableArray *). Laid, fragile et sujette aux erreurs. Ainsi, en utilisant le type générique est, en général, la solution la plus simple.

Alors ... si vous déclarez une méthode qui renvoie une instance d'une classe spécifique, typer explicitement. Si vous déclarez une méthode qui substituera et que override peut retourner une sous-classe et le fait qu'il retourne une sous-classe sera exposé à des clients, puis utilisez (id).

Pas besoin de déposer un bug -. Il y a déjà plusieurs


Notez que ObjC supporte maintenant co-variance limitée par le mot-clé instancetype.

i.e.. La méthode de tableau de + NSArray pourrait maintenant être déclaré comme suit:

+ (instancetype) array;

Et le compilateur traiterait [NSMutableArray array] comme un retour en NSMutableArray* [NSArray array] serait considéré comme retour NSArray*.

Vous pouvez déjà appeler -retain sur les pointeurs de type id sans coulée. Si vous utilisez un type spécifique superclasse, vous devrez lancer le pointeur chaque fois que vous appelez la méthode une sous-classe afin d'éviter les avertissements du compilateur. Utilisez id si le compilateur ne vous avertit pas et de mieux signifier votre intention.

(id) est souvent retourné afin de permettre aux objets d'être plus facilement sous-classé. Par exemple, dans les méthodes de initialiseur et de commodité, de retour (id) signifie que toute sous-classe ne doit pas remplacer les méthodes de la superclasse moins qu'il y ait une raison particulière de le faire.

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