在为 iOS 开发时,我应该何时使用 NSInteger
与 int?我在 Apple 示例代码中看到他们在将值作为参数传递给函数或从函数返回值时使用 NSInteger
(或 NSUInteger
)。
- (NSInteger)someFunc;...
- (void)someFuncWithInt:(NSInteger)value;...
但在一个函数中,他们只是使用 int
来跟踪一个值
for (int i; i < something; i++)
...
int something;
something += somethingElseThatsAnInt;
...
我已经阅读(被告知)NSInteger
是在 64 位或 32 位环境中引用整数的安全方法,那么为什么要使用 int
呢?
当您不知道您的代码可能在哪种处理器架构上运行时,您通常希望使用 NSInteger
,因此您可能出于某种原因需要尽可能大的整数类型,在 32 位系统上它只是一个 int
,而在 64 位系统上它是 long
。
除非您特别需要,否则我会坚持使用 NSInteger
而不是 int
/long
。
NSInteger
/NSUInteger
被定义为其中一种类型的 *dynamic typedef
*s,它们的定义如下:
#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
关于您应该为每种类型使用的正确格式说明符,请参阅 String Programming Guide's section on Platform Dependencies
为什么要使用 int
?
Apple 使用 int
是因为对于循环控制变量(仅用于控制循环迭代),int
数据类型很好,无论是数据类型大小还是它可以为循环保存的值。 这里不需要依赖于平台的数据类型。对于循环控制变量,即使是 16 位 int
大部分时间都可以。
Apple 将 NSInteger
用于函数返回值或函数参数因为在这种情况下数据类型 [大小] 很重要,因为您对函数所做的是与其他程序或与其他代码;在您的问题本身中查看我什么时候应该使用 NSInteger vs int? 的答案...
他们 [Apple] 在将值作为参数传递给函数或从函数返回值时使用 NSInteger(或 NSUInteger)。
OS X 是“LP64”。这意味着:
int
始终为 32 位。
long long
始终为 64 位。
NSInteger
和 long
始终是指针大小的。这意味着它们在 32 位系统上是 32 位,在 64 位系统上是 64 位。
NSInteger 存在的原因是许多遗留 API 错误地使用 int
而不是 long
来保存指针大小的变量,这意味着 API 在其 64 位版本中必须从 int
更改为 long
。换句话说,根据您是针对 32 位还是 64 位架构进行编译,API 将具有不同的函数签名。 NSInteger
打算用这些旧版 API 掩盖这个问题。
在您的新代码中,如果需要 32 位变量,请使用 int
,如果需要 64 位整数,请使用 long long
,如果需要指针大小的变量,请使用 long
或 NSInteger
。
int32_t
。如果您需要 64 位整数,请使用 int64_t
。如果您需要一个指针大小的变量,请使用 intptr_t
。
<stdint.h>
类型就是为此目的而存在的。
LP64
不保证 long long
是 64 位。 LP64
平台可以选择让 long long
为 128 位整数。
如果你深入研究 NSInteger 的实现:
#if __LP64__
typedef long NSInteger;
#else
typedef int NSInteger;
#endif
简单地说,NSInteger typedef 为您做了一个步骤:如果架构是 32 位,它使用 int
,如果是 64 位,它使用 long
。使用 NSInteger,您无需担心程序运行的架构。
long long
。所以所有数字类型都将使用相同的类型说明符。
NSLog("%@", @(1123));
NSLog("%li", (long)theNSInteger);
如果您需要将它们与常量值(例如 NSNotFound 或 NSIntegerMax)进行比较,则应该使用 NSIntegers,因为这些值在 32 位和 64 位系统上会有所不同,因此索引值、计数等:使用 NSInteger 或 NSUInteger。
在大多数情况下使用 NSInteger 并没有什么坏处,只是它占用了两倍的内存。对内存的影响非常小,但是如果您随时都有大量数字浮动,那么使用 int 可能会有所不同。
如果您确实使用 NSInteger 或 NSUInteger,则在使用格式字符串时,您需要将它们转换为长整数或无符号长整数,因为如果您尝试注销 NSInteger,就好像它具有已知长度一样,新的 Xcode 功能会返回警告。在将它们发送到类型为整数的变量或参数时,您同样应该小心,因为您可能会在此过程中失去一些精度。
总体而言,如果您不希望一次将数十万个它们放在内存中,那么使用 NSInteger 比不断担心两者之间的差异更容易。
在 iOS 上,目前使用 int
还是 NSInteger
并不重要。如果/当 iOS 迁移到 64 位时,它会更重要。
简单地说,NSInteger
是 32 位代码中的 int
(因此是 32 位长),而 long
是 64 位代码(64 位代码中的 long
是 64 位宽) ,但在 32 位代码中是 32 位)。使用 NSInteger
而不是 long
的最可能原因是不破坏现有的 32 位代码(使用 int
)。
CGFloat
有同样的问题:在 32 位(至少在 OS X 上),它是 float
;在 64 位上,它是 double
。
更新:随着 iPhone 5s、iPad Air、配备 Retina 的 iPad Mini 和 iOS 7 的推出,您现在可以在 iOS 上构建 64 位代码。
更新 2: 此外,使用 NSInteger
有助于 Swift 代码互操作性。
截至目前(2014 年 9 月),如果您还在为 arm64 构建应用程序,我建议在与 iOS API 等交互时使用 NSInteger/CGFloat
。这是因为当您使用 float
、long
和 int
类型时,您可能会得到意想不到的结果。
示例:FLOAT/DOUBLE 与 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
来解决这种情况,该类型用作基于编译/运行时环境的 int
或 long
。
int = 4 字节(固定与架构师的大小无关) NSInteger = 取决于架构师的大小(例如,对于 4 字节架构师 = 4 字节 NSInteger 大小)
不定期副业成功案例分享
int
可能更适合long
。也许您知道它不会超过某个范围,因此认为简单地使用int
会更节省内存。NSInteger
的是将值传入和传出指定它的 API。除此之外,它与 int 或 long 相比没有优势。至少使用 int 或 long 您知道在 printf 或类似语句中使用什么格式说明符。