什么时候使用NSInteger而不是int?

349

在iOS开发中,何时应该使用NSInteger而不是int?我看到在苹果的示例代码中,当将值作为参数传递给函数或从函数返回值时,他们使用NSInteger(或NSUInteger)。

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

但在函数内部,它们只是使用 int 来追踪一个值。

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

int something;
something += somethingElseThatsAnInt;
...

我已经阅读(或被告知)NSInteger是在64位或32位环境下引用整数的安全方式,那么为什么还需要使用int呢?

8个回答

326

当你不知道代码可能在什么处理器架构上运行时,通常应该使用NSInteger,因此你可能希望使用最大的整数类型,在32位系统上就是一个int,而在64位系统上则是一个long

除非你特别需要它们,否则我建议使用NSInteger而不是int/long

NSInteger/NSUInteger被定义为*动态typedef*到其中一个类型,并且它们的定义如下:

#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

关于每种类型所应使用的正确格式说明符,请参见字符串编程指南中有关平台依赖性的部分


4
此外,我想说最好使用 NSInteger,除非你特别需要 int 或 long int。 - v01d
4
使用“int”类型可能更适合于甚至比“long”类型更好。也许你知道它不会超出某个范围,因此认为直接使用“int”类型会更节省内存。 - Jacob Relkin
59
我不同意这个答案。我只会在需要与指定使用NSInteger的API交互时才使用它。除此之外,它与int或long相比没有任何优势。至少用int或long时,你知道在printf或类似语句中该使用哪些格式说明符。 - JeremyP
3
如果在64位系统中使用NSInteger存储长整型数据,而其他用户在32位系统中使用该数据类型,会发生什么?你可能不会注意到错误,但其他用户会受到影响。 - arielcamus
15
这是错误的。除非你有特定的原因,否则始终使用int。对于简单的整数,使用平台特定的定义只会使你的代码更难阅读。 - Glenn Maynard
显示剩余6条评论

44

为什么要使用 int

苹果公司使用 int 作为循环控制变量的数据类型(仅用于控制循环迭代),因为 int 数据类型在数据类型大小和其可容纳的循环值方面都可以胜任。这里没有必要使用平台相关的数据类型。对于循环控制变量,即使是 16 位的 int 大多数情况下也足够了。

苹果公司使用 NSInteger 作为函数返回值或函数参数时,因为在这种情况下数据类型[大小]很重要,因为你使用函数是为了与其他程序或代码通信/传递数据;请参见您问题中的答案:何时应该使用 NSInteger 而不是 int?

当将一个值作为参数传递给函数或从函数返回一个值时,他们 [苹果公司] 使用 NSInteger (或 NSUInteger)。


32

OS X是“LP64”的。这意味着:

int始终为32位。

long long始终为64位。

NSIntegerlong始终为指针大小。这意味着在32位系统上它们是32位,在64位系统上它们是64位。

存在NSInteger的原因是因为许多遗留API不正确地使用int代替long来保存指针大小的变量,这意味着API必须从int更改为long在它们的64位版本中。换句话说,API将根据您编译的是32位还是64位架构具有不同的函数签名。NSInteger旨在掩盖这些遗留API的问题。

在您的新代码中,如果需要32位变量,请使用int;如果需要64位整数,请使用long long;如果需要指针大小的变量,请使用longNSInteger


26
历史记录准确无误,但建议很糟糕。如果你需要一个32位变量,请使用int32_t。如果你需要一个64位整数,请使用int64_t。如果你需要一个指针大小的变量,请使用intptr_t - Stephen Canon
5
Stephen,你的建议是永远不要使用int、long或NSInteger吗? - Darren
8
不,我的建议是,如果您需要一个已知固定大小的整数类型,请永远不要使用它们。<stdint.h>类型就是为此而存在。 - Stephen Canon
3
Stephen,我的回答是针对问题“何时使用NSInteger与int”,而不是“32位整数的跨平台类型名称是什么”。如果有人试图在NSInteger和int之间做出决定,他们最好知道它们在所支持的平台上有多大。 - Darren
1
请注意,LP64 并不保证 long long 是 64 位。LP64 平台可以选择将 long long 设为 128 位整数。 - Stephen Canon
这个答案对于 Lion 仍然有效吗? - Duck

26

如果你深入了解NSInteger的实现:

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

简单来说,NSInteger 类型定义为你做了一步:如果架构是32位的,则使用 int,如果是64位,则使用 long。使用 NSInteger,您无需担心程序运行的架构。


15
不需要担心,因为正确的 NSInteger 格式说明符取决于架构。 - JeremyP
1
根据苹果手册,最简单的方法是将值转换为最大的数值类型“long long”。因此,所有数值类型都将使用相同的类型说明符。 - eonil
7
现在最简单的格式化方式就是将它们放在方括号里 - NSLog("%@", @(1123)); - eonil
1
你也可以进行类型转换:NSLog("%li", (long)theNSInteger); - Daniel
转换类型让我感到伤心。 - tomalbrc

11

如果需要将它们与常量值(如NSNotFound或NSIntegerMax)进行比较,例如索引值、计数等,则应使用NSIntegers,因为这些值在32位和64位系统上不同,因此应使用NSInteger或NSUInteger。

在大多数情况下,使用NSInteger不会有任何问题,除非它占用的内存是int的两倍。内存影响非常小,但如果您同时有大量数字在周围浮动,可能会有所影响。

如果您使用NSInteger或NSUInteger,则在使用格式字符串时,您需要将它们转换为长整数或无符号长整数,因为新版本的Xcode功能会返回警告,如果您尝试将NSInteger记录为具有已知长度。同样,在将它们发送到被输入为int的变量或参数时,应格外小心,因为在此过程中可能会失去一些精度。

总体而言,如果您不希望一次性将数百万个数字保存在内存中,那么更容易使用NSInteger而不必不断担心两者之间的差异。


10
在iOS上,目前使用intNSInteger都无所谓,但如果iOS迁移到64位,就会更加重要。简单来说,NSInteger在32位代码中是int(因此为32位长),而在64位代码中为long(64位宽度的long在64位代码中是64位的,在32位代码中则为32位)。使用NSInteger而不是long的最可能原因是为了不破坏现有32位代码(使用int)。CGFloat也存在同样的问题:在32位环境下(至少在OS X上),它是float;在64位环境下,它是double

更新:随着iPhone 5s、iPad Air、iPad Mini Retina和iOS 7的推出,现在可以在iOS上构建64位代码。

更新2:此外,使用NSInteger还有助于Swift代码的互操作性。


9
截止目前(2014年9月),如果你同时为arm64构建你的应用程序,我建议在与iOS API交互时使用NSInteger/CGFloat类型。这是因为当你使用floatlongint类型时,可能会得到意想不到的结果。 示例:FLOAT/DOUBLE vs CGFLOAT 以UITableView委托方法tableView:heightForRowAtIndexPath:为例。
在一个只有32位的应用程序中,如果写成以下形式,它将正常工作:
-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 44;
}

float是一个32位的值,而你返回的44也是一个32位的值。 然而,如果我们在64位的arm64架构中编译/运行相同的代码,44将成为一个64位的值。当期望返回32位的值时返回64位的值会导致行高出现意外。

您可以通过使用CGFloat类型来解决此问题。

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

此类型表示在32位环境下的32位float和在64位环境下的64位double。因此,使用此类型时,无论编译/运行时环境如何,方法始终会收到预期的类型。
对于需要整数的方法也是如此。这些方法将在32位环境中期望32位int值,在64位环境中期望64位long值。您可以通过使用类型NSInteger来解决此问题,该类型作为基于编译/运行时环境的intlong

如果我知道这个特定变量的值不可能包含大数值,并且我希望使用int,那么会在64位环境中正常工作吗?我认为应该可以,因为我从未见过像这样的for循环:for (int i=0;i<10;i++),无论在哪个环境下运行都不会出现任何错误行为。 - Chanchal Raj
只要没有将该变量转换为其他类型或使用/覆盖涉及该变量的第三方类和方法,使用int而不是NSInteger就可以了。 - Leon Lucardie

0

int = 4字节(固定大小,与架构无关) NSInteger = 取决于架构的大小(例如,对于4字节架构= 4字节NSInteger大小)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接