Quand devrais-je utiliser NSInteger
vs. int lors du développement pour iOS? Je vois dans l'exemple de code Apple qu'ils utilisent NSInteger
(ou NSUInteger
) lorsqu'ils transmettent une valeur en tant qu'argument à une fonction ou renvoient une valeur à partir d'une fonction.
- (NSInteger)someFunc;...
- (void)someFuncWithInt:(NSInteger)value;...
Mais dans une fonction, ils utilisent juste int
pour suivre une valeur
for (int i; i < something; i++)
...
int something;
something += somethingElseThatsAnInt;
...
J'ai lu (on m'a dit) que NSInteger
est un moyen sûr de référencer un entier dans un environnement 64 bits ou 32 bits, alors pourquoi utiliser int
?
Vous voulez généralement utiliser NSInteger
lorsque vous ne connaissez pas l'architecture de processeur sur laquelle votre code peut s'exécuter. Par conséquent, vous pouvez souhaiter le type le plus grand possible, qui sur les systèmes 32 bits n'est qu'un int
, alors que sur un système 64 bits, il s’agit d’un long
.
Je me contenterais d'utiliser NSInteger
au lieu de int
/long
sauf si vous en avez spécifiquement besoin.
NSInteger
/NSUInteger
sont définis comme * dynamiques typedef
* s selon l'un de ces types, et ils sont définis comme suit:
#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 Guide de programmation des chaînes sur les dépendances de plate-forme
Pourquoi utiliser int
du tout?
Apple utilise int
car, pour une variable de contrôle de boucle (utilisée uniquement pour contrôler les itérations de la boucle), le type de données int
convient, à la fois en taille et en valeur qu'il peut contenir pour votre boucle. Pas besoin de type de données dépendant de la plate-forme ici. Pour une variable de contrôle de boucle, même un int
16 bits fera la plupart du temps.
Apple utilise NSInteger
pour une valeur de retour de fonction ou pour un argument de fonction car dans ce cas, le type de données [taille] est important, car ce que vous faites avec une fonction est de communiquer/transmettre des données avec d'autres programmes ou avec d'autres éléments de code; voir la réponse à Quand devrais-je utiliser NSInteger vs int? dans votre question elle-même ...
ils [Apple] utilisent NSInteger (ou NSUInteger) lorsqu'ils transmettent une valeur en tant qu'argument d'une fonction ou renvoient un valeur d'une fonction.
OS X est "LP64". Cela signifie que:
int
est toujours 32 bits.
long long
est toujours 64 bits.
NSInteger
et long
sont 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.
NSInteger existe parce que de nombreuses API héritées utilisaient incorrectement int
au lieu de long
pour contenir des variables de la taille d'un pointeur, ce qui impliquait que les API devaient passer de int
à long
dans leurs versions 64 bits. En d'autres termes, une API aura différentes signatures de fonction selon que vous compilez pour des architectures 32 bits ou 64 bits. NSInteger
a l'intention de masquer ce problème avec ces API héritées.
Dans votre nouveau code, utilisez int
si vous avez besoin d’une variable 32 bits, long long
si vous avez besoin d’un entier 64 bits et long
ou NSInteger
si vous avez besoin d’un variable de la taille d'un pointeur.
Si vous creusez dans l'implémentation de NSInteger:
#if __LP64__
typedef long NSInteger;
#else
typedef int NSInteger;
#endif
Simplement, NSInteger typedef fait un pas pour vous: si l’architecture est 32 bits, il utilise int
, si elle est 64 bits, il utilise long
. Avec NSInteger, vous n'avez pas à vous soucier de l'architecture sur laquelle le programme est exécuté.
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. Par conséquent, les valeurs d'index, les nombres et autres: utilisez NSInteger ou NSUInteger.
Il n’est pas inutile d’utiliser NSInteger dans la plupart des cas, sauf que cela prend deux fois plus de mémoire. L'impact sur la mémoire est très faible, mais si vous avez beaucoup de chiffres en même temps, l'utilisation d'Ints peut faire la différence.
Si vous utilisez NSInteger ou NSUInteger, vous voudrez les convertir en entiers longs ou non signés lorsque vous utilisez des chaînes de format, car la nouvelle fonctionnalité Xcode renvoie un avertissement si vous essayez de déconnecter un NSInteger comme si sa longueur était connue. Vous devez également faire attention lorsque vous les envoyez à des variables ou à des arguments qui sont typés en tant qu'ints, car vous pourriez perdre un peu de précision dans le processus.
Dans l’ensemble, si vous ne vous attendez pas à en avoir simultanément des centaines de milliers dans la mémoire, il est plus facile d’utiliser NSInteger que de s’inquiéter constamment de la différence entre les deux.
Sur iOS, peu importe si vous utilisez int
ou NSInteger
. Ce sera plus important si/quand iOS passe à 64 bits.
Autrement dit, NSInteger
s sont int
s en code 32 bits (et donc de 32 bits de long) et long
s en code 64 bits (long
s en code 64 bits sont 64 bits, mais 32 bits en code 32 bits). La raison la plus probable pour utiliser NSInteger
au lieu de long
est de ne pas rompre le code 32 bits existant (qui utilise int
s).
CGFloat
a le même problème: sur 32 bits (du moins sur OS X), il s'agit de 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: De plus, l’utilisation de NSInteger
s facilite l’interopérabilité du code Swift.
Pour le moment (septembre 2014), je vous recommanderais d'utiliser NSInteger/CGFloat
lors de l'interaction avec les API iOS, etc., si vous construisez également votre application pour arm64. En effet, vous obtiendrez probablement des résultats inattendus lorsque vous utiliserez les types float
, long
et int
.
EXEMPLE: FLOTTEUR/DOUBLE vs CGFLOAT
A titre d'exemple, prenons la méthode de délégué UITableView tableView:heightForRowAtIndexPath:
.
Dans une application 32 bits uniquement, cela fonctionnera correctement s'il est écrit comme suit:
-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 44;
}
float
est une valeur de 32 bits et le 44 que vous renvoyez est une valeur de 32 bits. Cependant, si nous compilons/exécutons ce même morceau de code dans une architecture arm64 64 bits, la valeur 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 type CGFloat
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 44;
}
Ce type représente un float
32 bits dans un environnement 32 bits et un double
64 bits dans un environnement 64 bits. Par conséquent, lors de l'utilisation de ce type, la méthode recevra toujours le type attendu, quel que soit l'environnement de compilation/exécution.
Il en va de même pour les méthodes qui attendent des entiers. De telles méthodes attendent une valeur int
32 bits dans un environnement 32 bits et une valeur long
64 bits dans un environnement 64 bits. Vous pouvez résoudre ce problème en utilisant le type NSInteger
qui sert de int
ou de long
en fonction de l’environnement compile/runtime.
int = 4 octets (fixe quelle que soit la taille de l'architecte) NSInteger = dépend de la taille de l'architecte (par exemple, pour un architecte à 4 octets = 4 octets de taille NSInteger)