35 votes

Est-il prudent d'utiliser isKindOfClass: contre une instance de NSString pour déterminer le type?

À partir de la isKindOfClass: documentation de la méthode de NSObject:

Soyez prudent lorsque vous utilisez cette méthode sur des objets représentés par une classe de cluster. En raison de la nature de clusters de classe, l'objet que vous recevez peuvent pas toujours être le type que vous avez prévu.

La documentation procède ensuite à donner un exemple de pourquoi vous devriez ne jamais demander quelque chose comme la suite d'un NSArray exemple:

// DO NOT DO THIS!
if ([myArray isKindOfClass:[NSMutableArray class]])
{
    // Modify the object
}

Maintenant, pour donner un exemple d'une utilisation différente, disons que j'ai une instance de NSObject où je voulez savoir si j'ai un NSString ou NSArray.

Ces deux types sont des clusters de classe -, mais il semble d'après la documentation ci-dessus que le danger réside dans la réponse à isKindOfClass: être trop affirmatif (répondre par OUI, parfois, quand vous n'avez vraiment pas une mutable tableau) alors que de poser une question sur la simple appartenance à un cluster reste valide.

Un exemple:

NSObject *originalValue;

// originalValue gets set to some instance

if ( [originalValue isKindOfClass:[NSString class]] )
   // Do something with string

Cette hypothèse est correcte? Est-il vraiment sûr à utiliser isKindOfClass: contre la classe des instances de cluster afin de déterminer la composition? Je suis particulièrement intéressé par la réponse de l'omniprésence de la NSString, NSArray et NSDictionary, mais je serais intéressé de savoir si c'est généralisable.

32voto

drvdijk Points 3940

L'avertissement dans la documentation utilise l' NSMutableArray exemple. Supposons qu'un développeur utilise NSMutableArray comme classe de base pour sa propre mise en œuvre d'un nouveau type de tableau, CoolFunctioningArray. Cette CoolFunctioningArray par la conception n'est pas mutable. Toutefois, isKindOfClass sera de retour YES de isKindOfClass:[NSMutableArray class], ce qui est vrai, mais n'est pas.

isKindOfClass: sera de retour YES si le récepteur quelque part hérite de la classe passée en argument. isMemberOfClass: sera de retour YES seulement si le récepteur est une instance de la classe passée en argument, c'est a dire de ne pas y compris les sous-classes.

Généralement, isKindOfClass: n'est pas sûrs à utiliser pour le test d'adhésion. Si une instance renvoie YES pour isKindOfClass:[NSString class], vous savez seulement qu'il répondra à toutes les méthodes définies dans l' NSString classe, mais vous ne savez pas exactement ce que la mise en œuvre de ces méthodes pourrait le faire. Quelqu'un pourrait avoir sous-classé NSString de soulever une exception pour la méthode de longueur.

Je pense que vous pouvez utiliser isKindOfClass: pour ce genre de test, surtout si vous travaillez avec votre propre code qui vous (comme un bon Samaritain) sont conçus de telle façon qu'il répondra d'une manière que nous attendons tous (par exemple, la méthode de longueur d' NSString sous-classe de retourner la longueur de la chaîne, il représente le lieu de lever une exception). Si vous utilisez beaucoup de bibliothèques externes bizarre développeurs (comme le développeur de l' CoolFunctioningArray, ce qui devrait être tourné), vous devez utiliser l' isKindOfClass méthode avec précaution, et, de préférence, utiliser l' isMemberOfClass: méthode (éventuellement plusieurs fois pour tester l'appartenance d'un groupe de classes).

17voto

Granfalloner Points 156

Considérons le code suivant:

if ([dict isKindOfClass:[NSMutableDictionary class]])
{
    [(NSMutableDictionary*)dict setObject:@1 forKey:@"1"];
}

Je pense que le vrai but de la Pomme de note citée dans la question, c'est que ce code pourrait facilement conduire à des crash. Et le problème ici réside dans le numéro gratuit de transition avec CoreFoundation. Permet d'examiner plus en détails les 4 variantes:

NSDictionary* dict = [NSDictionary new];
NSMutableDictionary* dict = [NSMutableDictionary new];
CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

En effet, dans TOUS les 4 variantes de la véritable classe de la dict sera __NSCFDictionary. Et tous les 4 variantes va passer le test dans le premier extrait de code. Mais dans les 2 cas (NSDictionary et CFDictionaryRef déclarations), nous vous en collision avec le journal quelque chose comme ça:

* Résiliation d'application en raison de uncaught exception 'NSInternalInconsistencyException', la raison: '-[__NSCFDictionary setObject:forKey:]: la mutation de la méthode envoyé à immuables de l'objet

Donc, les choses deviennent un peu plus claires. Dans les 2 cas, nous sommes autorisés à modifier l'objet, et dans les 2 cas ne sont pas. Il dépend de la création de la fonction, et probablement de la mutabilité de l'état est suivi par __NSCFDictionary objet lui-même.

Mais pourquoi utilisons-nous la même classe pour mutable et des objets immuables? Réponse Possible - en raison de langage C limites (et CoreFoundation est une API en C, comme nous le savons). Ce problème ne nous ont en C? Les déclarations de changeant et immuable dictionnaire types devraient refléter les éléments suivants: CFMutableDictionaryRef type est un sous-type de CFDictionaryRef. Mais C n'a pas de mécanismes pour cela. Mais nous voulons être en mesure de passer à CFMutableDictionary objet en fonction attend CFDictionary objet, et de préférence, sans compilateur émettant des avertissements ennuyeux. Ce que nous avons à faire?

Permet d'avoir un oeil à la suite de CoreFoundation les déclarations de type:

typedef const struct __CFDictionary * CFDictionaryRef;
typedef struct __CFDictionary * CFMutableDictionaryRef;

Comme nous pouvons le voir ici, CFDictionary et CFMutableDictionary sont représentés par le même type, et ne diffèrent que par le modificateur const. Donc, ici commencent les ennuis.

La même chose s'applique généralement à NSArray trop, mais la situation est un peu plus compliqué. Lorsque vous créez NSArray ou NSMutableArray directement, vous obtiendrez des classes spéciales (__NSArrayI et __ _ _ NSArrayM respectivement). Pour cela, les classes de crash dans pas reproduit. Mais lorsque vous créez CFArray ou CFMutableArray, les choses restent les mêmes. Donc soyez prudent!

13voto

newacct Points 42530

Vous êtes à la lecture de l'avertissement de mal. Tout ce qu'elle dit, c'est qu'il est représenté en interne comme quelque chose qui est mutable, ne signifie pas que vous devriez essayer de les transformer, parce que la mutation il pourrait violer le contrat entre vous et celui que vous avez obtenu le tableau à partir d'; il peut violer leurs hypothèses que vous ne serez pas les transformer.

Cependant, le test donné par isKindOfClass: est certainement encore valide. Si [something isKindOfClass:[NSMutableArray class]], puis il est une instance de l' NSMutableArray ou sous-classe de celle-ci, ce qui signifie en gros c'est mutable.

5voto

Rob N Points 2142

C'est un étrange avertissement dans l'Apple docs. C'est comme de dire: "attention cette fonction, parce que si vous l'utilisez avec d'autres de code cassé, ça ne marchera pas." Mais vous pouvez dire avec quoi que ce soit.

Si la conception suit le principe de substitution, dont il se doit, puis isKindOfClass fonctionne. (wikipédia: Principe de Substitution de Liskov)

Si un peu de code, viole le principe, en retournant une instance d'un NSMutableArray sous-classe qui ne répond pas à la addObject: et autres NSMutableArray méthodes, alors que le code a un problème... sauf si c'est juste une petite échelle temporaire hack...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X