Quand utiliser NSInteger vs int

344

Quand dois-je utiliser NSIntegervs. int lors du développement pour iOS? Je vois dans l'exemple de code Apple qu'ils utilisent NSInteger(ou NSUInteger) lorsqu'ils passent une valeur comme argument à une fonction ou retournent une valeur à partir d'une fonction.

- (NSInteger)someFunc;...
- (void)someFuncWithInt:(NSInteger)value;...

Mais dans une fonction qu'ils utilisent simplement intpour suivre une valeur

for (int i; i < something; i++)
...

int something;
something += somethingElseThatsAnInt;
...

J'ai lu (on m'a dit) que NSIntegerc'est un moyen sûr de référencer un entier dans un environnement 64 bits ou 32 bits, alors pourquoi l'utiliser int?

Shizam
la source

Réponses:

322

Vous voulez généralement utiliser NSIntegerlorsque vous ne savez pas sur quel type d'architecture de processeur votre code peut fonctionner, vous pouvez donc pour une raison quelconque vouloir le plus grand type entier possible, qui sur les systèmes 32 bits n'est qu'un int, tandis que sur un 64 bits système c'est un long.

Je préfère utiliser NSIntegerau lieu de int/, longsauf si vous en avez spécifiquement besoin.

NSInteger/ NSUIntegersont définis comme * dynamiques typedef* pour l'un de ces types, et ils sont définis comme ceci:

#if __LP64__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif

En ce qui concerne le spécificateur de format correct que vous devez utiliser pour chacun de ces types, consultez la section du Guide de programmation des chaînes sur les dépendances de la plate-forme

Jacob Relkin
la source
4
De plus, je dirais qu'il est préférable d'utiliser NSInteger sauf si vous avez spécifiquement besoin de int ou long int.
v01d
4
@Shizam Il est possible que l'utilisation d'un intsoit mieux adaptée à un long. Peut-être savez-vous qu'il ne dépassera pas une certaine plage et pensez donc qu'il sera plus efficace en mémoire à utiliser simplement int.
Jacob Relkin
58
Je ne suis pas d'accord avec cette réponse. La seule chose que j'utiliserais NSIntegerpour passer des valeurs vers et depuis une API qui le spécifie. En dehors de cela, il n'a aucun avantage sur un int ou un long. Au moins avec un int ou un long, vous savez quels spécificateurs de format utiliser dans un printf ou une instruction similaire.
JeremyP
3
Que se passe-t-il si vous avez besoin de stocker un long et que vous utilisez NSInteger pendant que vous travaillez dans un système 64b mais qu'un autre utilisateur utilise un système 32b? Vous ne remarquerez pas l'échec, mais l'utilisateur le remarquera.
arielcamus
14
C'est à l'envers. Utilisez toujours int, sauf indication contraire. L'utilisation de définitions spécifiques à la plate-forme pour des entiers simples ne fait que rendre votre code plus difficile à lire.
Glenn Maynard
44

Pourquoi utiliser intdu tout?

Apple utilise intcar pour une variable de contrôle de boucle (qui n'est utilisée que pour contrôler les itérations de boucle), le inttype de données est correct, à la fois en taille de type de données et en valeurs qu'il peut contenir pour votre boucle. Pas besoin de type de données dépendant de la plateforme ici. Pour une variable de contrôle de boucle, même un 16 bits intfera l'affaire la plupart du temps.

Apple utilise NSIntegerpour une valeur de retour de fonction ou pour un argument de fonction parce que dans ce cas, le type de données [taille] est important , parce que ce que vous faites avec une fonction est de communiquer / transmettre des données avec d'autres programmes ou avec d'autres morceaux de code; voir la réponse à Quand dois-je utiliser NSInteger vs int? dans votre question elle-même ...

ils [Apple] utilisent NSInteger (ou NSUInteger) lorsqu'ils passent une valeur comme argument à une fonction ou retournent une valeur à partir d'une fonction.

Seulement toi
la source
32

OS X est "LP64". Cela signifie que:

int est toujours de 32 bits.

long long est toujours 64 bits.

NSIntegeret longsont toujours de la taille d'un pointeur. Cela signifie qu'ils sont 32 bits sur les systèmes 32 bits et 64 bits sur les systèmes 64 bits.

La raison pour laquelle NSInteger existe est parce que de nombreuses API héritées sont utilisées de manière incorrecte intau lieu de longcontenir des variables de taille de pointeur, ce qui signifie que les API doivent passer de intà longdans leurs versions 64 bits. En d'autres termes, une API aurait différentes signatures de fonction selon que vous compilez pour des architectures 32 bits ou 64 bits. NSIntegera l'intention de masquer ce problème avec ces API héritées.

Dans votre nouveau code, utilisez intsi vous avez besoin d'une variable 32 bits, long longsi vous avez besoin d'un entier 64 bits et longou NSIntegersi vous avez besoin d'une variable de la taille d'un pointeur.

Darren
la source
25
L'histoire est parfaite, mais le conseil est terrible. Si vous avez besoin d'une variable 32 bits, utilisez int32_t. Si vous avez besoin d'un entier 64 bits, utilisez int64_t. Si vous avez besoin d'une variable de la taille d'un pointeur intptr_t.
Stephen Canon
5
Stephen, votre conseil est de ne jamais utiliser int, long ou NSInteger alors?
Darren
7
Non, mon conseil est de ne jamais les utiliser si vous avez besoin d'un type entier de taille fixe connue. Les <stdint.h>types existent à cet effet.
Stephen Canon
3
Stephen, ma réponse était en réponse à la question "Quand utiliser NSInteger vs int", pas "quel est le nom de type multiplateforme d'un entier 32 bits". Si quelqu'un essaie de décider entre NSInteger et int, il pourrait aussi bien savoir à quel point il est grand sur les plates-formes qu'il prend en charge.
Darren
1
Notez également que LP64cela ne garantit pas que long longc'est 64 bits. Une LP64plate-forme pourrait choisir d'avoir long longun entier de 128 bits.
Stephen Canon
26

Si vous creusez dans l'implémentation de NSInteger:

#if __LP64__
typedef long NSInteger;
#else
typedef int NSInteger;
#endif

Simplement, le typedef NSInteger fait une étape pour vous: si l'architecture est 32 bits, il utilise int, s'il est 64 bits, il utilise long. En utilisant NSInteger, vous n'avez pas à vous soucier de l'architecture sur laquelle le programme s'exécute.

Evan Mulawski
la source
14
Vous devez vous inquiéter, car le spécificateur de format correct pour NSInteger dépend de l'architecture.
JeremyP
Selon le manuel Apple, la manière la plus simple consiste à attribuer une valeur au plus grand type numérique long long. Ainsi, tous les types numériques utiliseront le même spécificateur de type.
Eonil
6
Maintenant, le moyen le plus simple de formater est simplement de les mettre en boîte -NSLog("%@", @(1123));
Eonil
1
vous pouvez également le lancer:NSLog("%li", (long)theNSInteger);
Daniel
le casting me rend triste
tomalbrc
9

Vous devez utiliser NSIntegers si vous devez les comparer à des valeurs constantes telles que NSNotFound ou NSIntegerMax, car ces valeurs différeront sur les systèmes 32 bits et 64 bits, donc indexez les valeurs, les nombres et autres: utilisez NSInteger ou NSUInteger.

Cela ne fait pas de mal d'utiliser NSInteger dans la plupart des cas, sauf qu'il prend deux fois plus de mémoire. L'impact de la mémoire est très faible, mais si vous avez une énorme quantité de nombres flottant à un moment donné, cela peut faire une différence d'utiliser des ints.

Si vous utilisez NSInteger ou NSUInteger, vous souhaiterez les convertir en entiers longs ou en entiers longs non signés lors de l'utilisation de chaînes de format, car la nouvelle fonctionnalité Xcode renvoie un avertissement si vous essayez de vous déconnecter d'un NSInteger comme s'il avait une longueur connue. De même, vous devez être prudent lorsque vous les envoyez à des variables ou des arguments qui sont tapés comme des entiers, car vous risquez de perdre une certaine précision dans le processus.

Dans l'ensemble, si vous ne vous attendez pas à en avoir des centaines de milliers en mémoire à la fois, il est plus facile d'utiliser NSInteger que de vous soucier constamment de la différence entre les deux.

Cendre
la source
9

À partir d'aujourd'hui (septembre 2014), je recommanderais d'utiliser NSInteger/CGFloatlors de l'interaction avec les API iOS, etc. si vous créez également votre application pour arm64. En effet , vous obtiendrez probablement des résultats inattendus lorsque vous utilisez les float, longet les inttypes.

EXEMPLE: FLOAT / DOUBLE vs CGFLOAT

Comme exemple, nous prenons la méthode déléguée UITableView tableView:heightForRowAtIndexPath:.

Dans une application 32 bits uniquement, cela fonctionnera correctement s'il est écrit comme ceci:

-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 44;
}

floatest une valeur 32 bits et le 44 que vous renvoyez est une valeur 32 bits. Cependant, si nous compilons / exécutons ce même morceau de code dans une architecture arm64 64 bits, le 44 sera une valeur 64 bits. Le renvoi d'une valeur 64 bits lorsqu'une valeur 32 bits est attendue donnera une hauteur de ligne inattendue.

Vous pouvez résoudre ce problème en utilisant le CGFloattype

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 44;
}

Ce type représente un 32 bits floatdans un environnement 32 bits et un 64 bits doubledans un environnement 64 bits. Par conséquent, lorsque vous utilisez ce type, la méthode reçoit toujours le type attendu, quel que soit l'environnement de compilation / d'exécution.

Il en va de même pour les méthodes qui attendent des entiers. Ces méthodes attendent une intvaleur 32 bits dans un environnement 32 bits et une valeur 64 bits longdans un environnement 64 bits. Vous pouvez résoudre ce cas en utilisant le type NSIntegerqui sert de intou longbasé sur l'environnement de compilation / exécution.

Léon Lucardie
la source
Que faire si je sais que la valeur de cette variable particulière ne peut pas contenir de valeurs de grand nombre et que je souhaite donc utiliser int. Cela fonctionnera-t-il bien à la fois dans un environnement 64 bits. Je pense que cela devrait aussi, car je n'ai pas vu de boucle for comme celle-ci: for (int i = 0; i <10; i ++) faisant un mauvais comportement quel que soit l'environnement dans
lequel
@Chanchal Raj Tant qu'il n'y a pas de transtypage ou de conversion vers d'autres types ou d'utilisation / redéfinition de classes et méthodes tierces impliquant cette variable, l'utilisation d'un int au lieu de NSInteger sera très bien.
Leon Lucardie
9

Sur iOS, peu importe si vous utilisez intou NSInteger. Cela importera plus si / quand iOS passe à 64 bits.

En termes simples, NSIntegers sont ints en code 32 bits (et donc 32 bits de long) et longs en code 64 bits ( longs en code 64 bits ont une largeur de 64 bits, mais 32 bits en code 32 bits). La raison la plus probable pour utiliser NSIntegerau lieu de longest de ne pas casser le code 32 bits existant (qui utilise ints).

CGFloata le même problème: sur 32 bits (au moins sur OS X), c'est float; sur 64 bits, c'est double.

Mise à jour: avec l'introduction de l'iPhone 5s, de l'iPad Air, de l'iPad Mini avec Retina et d'iOS 7, vous pouvez désormais créer du code 64 bits sur iOS.

Mise à jour 2: En outre, l'utilisation de NSIntegers aide à l'interopérabilité du code Swift.

MaddTheSane
la source
0

int = 4 octets (fixe quelle que soit la taille de l'architecte) NSInteger = dépend de la taille de l'architecte (par exemple pour 4 octets architecte = 4 octets NSInteger taille)

Garçon Ankit
la source