Pourquoi une variable NSInteger doit-elle être convertie en long lorsqu'elle est utilisée comme argument de format?

143
NSInteger myInt = 1804809223;
NSLog(@"%i", myInt); <==== 

Le code ci-dessus produit une erreur:

Les valeurs de type «NSInteger» ne doivent pas être utilisées comme arguments de format; ajoutez plutôt une distribution explicite à 'long'

Le NSLogmessage corrigé est en fait NSLog(@"%lg", (long) myInt);. Pourquoi dois-je convertir la valeur entière de myInten longsi je veux que la valeur s'affiche?

Daniel Lee
la source
1
@DanielLee, si vous utilisez NSLog(@"%ld", (long) myInt);, le longcasting est de faire correspondre le lqualificatif de %ld, mais tout cela est inutile car NSLog(@"%d", myInt);c'est suffisant (étant donné que nous pouvons voir que ce myIntn'est pas le cas long. En bout de ligne, vous lancez myIntsi vous utilisez un qualificatif long au format chaîne, mais pas besoin d'utiliser le qualificatif de format de chaîne longue ou de longtranstyper ici.
Rob
1
Apparemment, ce n'est pas vrai que NSLog (@ "% i", myInt); est suffisant car vous obtiendrez le message d'erreur comme je l'ai montré ci-dessus.
Daniel Lee
2
@DanielLee Voir le commentaire de Martin R. Vous avez publié votre question avec la balise iOS (où NSIntegern'est pas long), mais il semble que vous compiliez avec la cible OS X (où NSInteger est long ).
Rob
Ahh je vois. Je ne savais pas qu'iOS et OSX rendraient le NSInteger différent en bits et en type.
Daniel Lee

Réponses:

193

Vous obtenez cet avertissement si vous compilez sur OS X (64 bits), car sur cette plate-forme NSIntegerest défini comme longet est un entier 64 bits. Le %iformat, en revanche, est pourint , qui est 32 bits. Ainsi, le format et le paramètre réel ne correspondent pas en taille.

Puisqu'il NSIntegers'agit de 32 bits ou 64 bits, selon la plate-forme, le compilateur recommande d'ajouter un cast à longgénéralement.

Mise à jour: Étant donné qu'iOS 7 prend désormais également en charge 64 bits, vous pouvez obtenir le même avertissement lors de la compilation pour iOS.

Martin R
la source
1
J'obtiens cette erreur sur iOS 7. Étant donné que seul le dernier iPhone 5S est 64 bits si je le définis, cela causera-t-il un problème sur les anciens appareils 32 bits?
Pritesh Desai
25
@BartSimpson: Avec un cas explicite à "long", comme dans NSLog(@"%ld", (long) myInt), cela fonctionne correctement sur 32 bits et 64 bits.
Martin R
@MartinR si nous faisons du casting, pourquoi ne pas simplement utiliser long en premier lieu?
William Entriken
3
@FullDecent: Bien sûr , vous pouvez travailler avec longtemps ici: long myInt = [myNumber longValue];. Mais de nombreuses méthodes (Core) Foundation utilisent NS (U) Integer comme paramètre ou valeur de retour, de sorte que le problème général demeure. Il peut également être judicieux dans votre application d'utiliser NS (U) Integer pour obtenir une plus grande plage disponible sur les appareils 64 bits.
Martin R
39

Vous n'avez pas besoin de convertir quoi que ce soit si vos spécificateurs de format correspondent à vos types de données. Voir la réponse de Martin R pour plus de détails sur la façon dont NSIntegerest définie en termes de types natifs.

Donc, pour le code destiné à être construit pour les environnements 64 bits, vous pouvez écrire vos instructions de journal comme ceci:

NSLog(@"%ld",  myInt); 

tandis que pour les environnements 32 bits, vous pouvez écrire:

NSLog(@"%d",  myInt); 

et tout fonctionnera sans moulages.

Une des raisons d'utiliser des casts de toute façon est que le bon code a tendance à être porté sur toutes les plates-formes, et si vous cast vos variables explicitement, il compilera proprement sur 32 et 64 bits:

NSLog(@"%ld",  (long)myInt);

Et notez que cela est vrai non seulement pour les instructions NSLog, qui ne sont que des aides au débogage après tout, mais aussi pour [NSString stringWithFormat:]les divers messages dérivés, qui sont des éléments légitimes du code de production.

Monolo
la source
1
Alors maintenant que ce hack est requis, est-il toujours préférable d'utiliser NSInteger en premier lieu?
William Entriken
@FullDecent Il s'agit uniquement d'un problème dans le code qui est interprété au moment de l'exécution, comme les chaînes de format. Tout code compilé tire parti du typedef NSInteger.
Monolo
Il est recommandé d'utiliser NSInteger, car il existe de bonnes raisons pour lesquelles il est défini de la manière dont il est défini.
gnasher729
22

Au lieu de passer un NSInteger à NSLog, passez simplement un NSNumber. Cela contournera tous les casts et choisira le bon spécificateur de format de chaîne.

NSNumber foo = @9000;
NSLog(@"foo: %@", foo);
NSInteger bar = 9001;
NSLog(@"bar: %@", @(bar));

Cela fonctionne également pour NSUIntegers sans avoir à vous en soucier. Voir la réponse à NSInteger et NSUInteger dans un environnement mixte 64 bits / 32 bits

Orkoden
la source
2
Je suppose que la réponse choisie est techniquement la meilleure réponse à la question, mais si vous voulez savoir comment éviter de lancer chaque occurrence et empêcher les avertissements, je trouve que c'est la meilleure solution.
Daniel Wood
0

Il garde l'avertissement lors de l'utilisation NSLog(@"%ld", (long)myInt);, mais arrête l'avertissement après la déclaration de modification long myInt = 1804809223;dans iOS 10.

Yao Li
la source
-2

OS X utilise plusieurs types de données (NSInteger, NSUInteger, CGFloat et CFIndex) pour fournir un moyen cohérent de représenter les valeurs dans les environnements 32 et 64 bits. Dans un environnement 32 bits, NSInteger et NSUInteger sont définis respectivement comme int et unsigned int. Dans les environnements 64 bits, NSInteger et NSUInteger sont définis respectivement comme long et unsigned long. Pour éviter d'avoir à utiliser différents spécificateurs de type printf en fonction de la plate-forme, vous pouvez utiliser les spécificateurs indiqués dans ce lien pour les environnements 32 bits et 64 bits.

Saheb Singh
la source