40 votes

Récupérer le type de NSNumber

Je veux obtenir le type de l'instance NSNumber.

Je l'ai découvert sur http://www.cocoadev.com/index.pl?NSNumber ceci :

 NSNumber \*myNum = \[\[NSNumber alloc\] initWithBool:TRUE\];

 if (\[\[myNum className\] isEqualToString:@"NSCFNumber"\]) {
  // process NSNumber as integer
 } else if  (\[\[myNum className\] isEqualToString:@"NSCFBoolean"\]) {
  // process NSNumber as boolean
 }

Ok, mais cela ne fonctionne pas, le [myNum className] n'est pas reconnu par le compilateur. Je compile pour l'iPhone.

11 votes

Ceci est potentiellement fragile. NSCFNumber et NSCFBoolean sont privés et il n'y a aucune garantie que ceux-ci continueront à être les noms de classe dans le futur.

0 votes

Que diriez-vous de if([maNum classe] == [[NSNumber nombreWithBool:YES] classe])

0 votes

@GlennHowes Aucune garantie que cela donnera le bon résultat. Vous faites la même supposition que @okami ; que les interfaces privées ne changeront jamais.

75voto

Dave DeLong Points 156978

Je recommande d'utiliser le -[NSNumber objCType] méthode.

Il vous permet de faire :

NSNumber * n = [NSNumber numberWithBool:YES];
if (strcmp([n objCType], @encode(BOOL)) == 0) {
    NSLog(@"this is a bool");
} else if (strcmp([n objCType], @encode(int)) == 0) {
    NSLog(@"this is an int");
}

Pour plus d'informations sur les codages de type, consultez la page Référence pour l'exécution d'Objective-C .

0 votes

+1 conseil cool ! C'est étrange qu'il n'y ait pas une méthode pour sortir ceci de NSNumber beaucoup plus facilement.

17 votes

Cela ne fonctionne pas - du moins sous iOS : (lldb) p (char *)[[NSNumber numberWithBool:YES] objCType] - il encode le bool comme un char en interne (ce qui est correct au niveau de la machine, mais pas au niveau intentionnel)

10 votes

Cette information est datée, elle ne fonctionnera pas sur les appareils et simulateurs iOS 64 bits et ne doit donc pas être utilisée. Elles peuvent conduire à des problèmes très difficiles à trouver qui ne se produisent que sur les appareils iOS 64 bits.

48voto

Chris Points 13472

Vous pouvez obtenir le type de cette façon, sans avoir à comparer les chaînes de caractères :

CFNumberType numberType = CFNumberGetType((CFNumberRef)someNSNumber);

numberType sera alors l'un des suivants :

enum CFNumberType {
   kCFNumberSInt8Type = 1,
   kCFNumberSInt16Type = 2,
   kCFNumberSInt32Type = 3,
   kCFNumberSInt64Type = 4,
   kCFNumberFloat32Type = 5,
   kCFNumberFloat64Type = 6,
   kCFNumberCharType = 7,
   kCFNumberShortType = 8,
   kCFNumberIntType = 9,
   kCFNumberLongType = 10,
   kCFNumberLongLongType = 11,
   kCFNumberFloatType = 12,
   kCFNumberDoubleType = 13,
   kCFNumberCFIndexType = 14,
   kCFNumberNSIntegerType = 15,
   kCFNumberCGFloatType = 16,
   kCFNumberMaxType = 16
};
typedef enum CFNumberType CFNumberType;

17 votes

Malheureusement, il n'y a pas de kCFNumberBoolType pour distinguer les valeurs booléennes des caractères, donc cela ne fonctionne pas pour tous les cas.

0 votes

KCFNumberCharType == CFNumberGetType((__bridge CFNumberRef)nsValue) et 0 == strcmp([nsValue objCType], "c") fonctionnent tous deux sur les systèmes 32 et 64 bits, mais si vous avez la malchance de supporter un code qui s'en soucie, je pense que la première solution est légèrement plus sûre.

0 votes

@MichaelManner vous devriez utiliser Unichar et/ou NSValue pour stocker les caractères, et non NSNumber.

30voto

Jakob Egger Points 4282

Si tout ce que vous voulez, c'est faire la différence entre les booléens et n'importe quoi d'autre, vous pouvez utiliser le fait que les booléens NSNumbers renvoient toujours une instance partagée :

NSNumber *num = ...;
if (num == (void*)kCFBooleanFalse || num == (void*)kCFBooleanTrue) {
   // num is boolean
} else {
   // num is not boolean
}

2 votes

+1 pour l'ingéniosité, mais je ne la recommanderais pas pour du code de production (la solution est bien trop fragile...).

1 votes

Cela dépend du mode de défaillance. Je l'utilise pour afficher "VRAI" ou "FAUX" pour les booléens ; dans le cas peu probable où Apple modifierait ce détail d'implémentation, mon application afficherait "1" ou "0" à la place ; je peux vivre avec ça. (Surtout qu'il n'y a pas d'autre solution que d'écrire votre propre sous-classe NSNumber qui garde la trace du type avec lequel elle a été créée).

0 votes

Accordé : votre solution est la meilleure jusqu'à présent et, comme vous le soulignez à juste titre, la seule (concernant la sous-classe NSNumber : Je suis venu ici parce que je voulais distinguer les booléens des entiers dans une plist, ça ne me servirait à rien...). Il faut aussi admettre qu'en cas d'échec, la solution renverrait des valeurs correctes truthy et falsy. Le fait est que, soit vous vous souciez de savoir si une valeur est un booléen (et vous ne pouvez pas accepter les faux négatifs), soit vous ne vous en souciez pas (alors pourquoi s'en soucier ? :) ).

9voto

user303014 Points 89

NSNumber ne garantit pas explicitement que le type retourné correspondra à la méthode utilisée pour le créer, donc le faire tout court est probablement une mauvaise idée.

Cependant, vous pourriez probablement faire quelque chose comme ceci (vous pourriez aussi comparer à objc_getClass("NSCFNumber") etc., mais on peut dire qu'elle est plus portable) :

Class boolClass = [[NSNumber numberWithBool:YES] class];
/* ... */
if([myNum isKindOfClass:boolClass]) {
  /* ... */
}

0 votes

Dans mes tests cette méthode échoue sur l'iPad avec iOS 8.4

0 votes

C'est mauvais car ce n'est pas une boolClass mais une numberClass avec une valeur bool.

4voto

Quinn Taylor Points 29688

La raison pour laquelle le compilateur vous avertit et que cela ne fonctionne pas est que -[NSObject className] est déclaré dans une catégorie sur NSObject sur Mac OS X (dans NSScriptClassDescription.h) et non déclaré sur iPhone. (Il ne supporte pas AppleScript, évidemment). NSStringFromClass([myNum class]) est ce que vous devez utiliser pour être sûr sur toutes les plateformes. Il y a de fortes chances que -className est déclaré comme un simple emballage autour de NSStringFromClass() de toute façon...

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