C語言中的整型有很多種,到了 Objective-C 中,除了這些類型外,又加入了 NSIntenger 等。
- short
- int
- long
- long long
- NSIntenger
- int8_t
- int16_t
- int32_t
- int64_t
Long
在C語言中有時候我們需要比 int(32bit)范圍更大的整型狈网。而 long 的位數(shù)是由編譯器決定的,大部分C語言的編譯器會根據(jù)系統(tǒng)的位數(shù)來決定 long 的位數(shù)笨腥,在32位中 long 是32位(LONG_MAX=2147483647)拓哺,在64位中 long 是64位(LONG_MAX=9223372036854775807)。這種情況下脖母,在32位系統(tǒng)中我們就需要使用 long long int(64bit)士鸥,而在64位系統(tǒng)中則應(yīng)該使用 long int(64bit)。這對程序員來說是個很棘手的問題谆级,我們不可能根據(jù)系統(tǒng)位數(shù)來決定使用哪種類型烤礁。這時就應(yīng)該使用固定位數(shù)的 int8_t、int16_t肥照、int32_t脚仔、int64_t。他們的范圍分別對應(yīng)8bit舆绎、16bit鲤脏、32bit、64bit吕朵。所以猎醇,如果想要在多種平臺上都獲得較大的位數(shù),則應(yīng)該使用 int64_t边锁。
NSIntenger
NSIntenger 是 Objective-C 中新的整型數(shù)姑食,在 iOS 開發(fā)中我們也習(xí)慣使用 NSIntenger波岛。他的源代碼如下茅坛。
#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
可以發(fā)現(xiàn),NSIntenger 與 long 的特性相似,位數(shù)都是根據(jù)系統(tǒng)位數(shù)來確定贡蓖。但是唯一的差距是曹鸠,NSIntenger 是通過預(yù)編譯來確定 NSIntenger 是重定義 int 還是 long,這在 Cocoa 的控制之下斥铺。NSInteger 的存在的原因是因為許多遺留的 API 使用不當(dāng)彻桃。在 iOS 沒有64位系統(tǒng)之前,如果 API 用 int 來持有有效的指針晾蜘,由于指針的位數(shù)是與系統(tǒng)位數(shù)保持一致的邻眷。這意味在64位系統(tǒng)中該 API 的不得不從 int 改為 long 才能正確記錄所有有效指針(如下代碼)。如果使用 NSInteger 則無需考慮這樣的問題剔交。根據(jù)我謹(jǐn)慎的猜測肆饶,long 的位數(shù)與系統(tǒng)位數(shù)保持一致,也應(yīng)該是由于這樣的原因岖常。但是相對于 NSIntenger 這種通過預(yù)編譯來控制的方式驯镊,long 對于蘋果來說明顯更加不可控。
隱患
知道了上述這些差異后竭鞍,就來舉兩個例子說明不了解這些差異可能帶來的隱患板惑。
用 int 儲存指針地址
- (NSUInteger)hash {
long v1 = (long)((void *)_selector);
long v2 = (long)_target;
return v1 ^ v2;
}
這是 YYKit 中的一段代碼。這里如果使用 int 去儲存函數(shù)指針地址偎快,在64位的系統(tǒng)中則會有一半的地址因為越界變成 INT_MAX冯乘。
格式化 NSInteger
當(dāng)我們打印或格式化 NSInteger 時可能會時而遇到這樣的警告,時而又不會晒夹。
NSInteger test;
NSLog(@"%d", test);
這是因為往湿,當(dāng)我們用32位的手機(iPhone5s 以下)調(diào)試的時候,NSIntenger 其實就是int惋戏,所以沒有警告领追。當(dāng)我們拔掉數(shù)據(jù)線時,Xcode 會默認(rèn)選中“Generic iOS Device”响逢,而“Generic iOS Device”模擬的是64位的真機绒窑,這時 NSIntenger 就是 long,所以才會出現(xiàn)警告舔亭。當(dāng)我們切換模擬器時也會出現(xiàn)這樣的情況些膨。這就是為什么這樣的警告時而出現(xiàn)時而消失的原因。
占用空間
通過如下代碼對各種整型的占用的大小進(jìn)行打印钦铺。
short _short = 0;
int _int = 0;
long _long = 0;
long long _long_long = 0;
NSInteger _NSIntenger = 0;
int8_t _int8_t = 0;
int16_t _int16_t = 0;
int32_t _int32_t = 0;
int64_t _int64_t = 0;
NSLog(@"\nshort:%lu\n"
"int:%lu\n"
"long:%lu\n"
"long long:%lu\n"
"NSInteger:%lu\n"
"int8_t:%lu\n"
"int16_t:%lu\n"
"int32_t:%lu\n"
"int64_t:%lu\n",
sizeof(_short),
sizeof(_int),
sizeof(_long),
sizeof(_long_long),
sizeof(_NSIntenger),
sizeof(_int8_t),
sizeof(_int16_t),
sizeof(_int32_t),
sizeof(_int64_t));
打印出來的結(jié)果如下:
64bit
short:2
int:4
long:8
long long:8
NSInteger:8
int8_t:1
int16_t:2
int32_t:4
int64_t:8
32bit
short:2
int:4
long:4
long long:8
NSInteger:4
int8_t:1
int16_t:2
int32_t:4
int64_t:8
根據(jù)打印信息我們發(fā)現(xiàn) short订雾、int 和 long long 不管在32位還是64位系統(tǒng)中他們的大小都是固定的。查看代碼發(fā)現(xiàn)矛洞,其實 int16_t洼哎、int32_t烫映、int64_t 就是簡單的重定義了 short、int噩峦、long long 而已锭沟。
更多
除了這些之外,Objective-C 中還重定義了大量類似的整數(shù)類型识补。
typedef unsigned char UInt8;
typedef signed char SInt8;
typedef unsigned short UInt16;
typedef signed short SInt16;
#if __LP64__
typedef unsigned int UInt32;
typedef signed int SInt32;
#else
typedef unsigned long UInt32;
typedef signed long SInt32;
#endif
UInt8族淮、SInt8、UInt16凭涂、SInt16祝辣、UInt32、SInt32 分別對應(yīng) uint8_t切油、int8_t较幌、uint16_t、int16_t白翻、NSInteger乍炉、NSUInteger。前四種相對后四種來說使用駝峰命名法滤馍,更符合Objective-C的風(fēng)格岛琼。他們均在 Core Framework 中大量使用。
使用
在 iOS 開發(fā)中我們習(xí)慣使用 NSIntenger巢株,因為 Cocoa 的 API 返回的數(shù)據(jù)類型大多是 NSIntenger槐瑞。而且,如果將來 iPhone 推出了新的系統(tǒng)架構(gòu)阁苞,NSIntenger 也可以保證你老項目中任何奇怪的代碼都不會出現(xiàn)隱患困檩。對于幾種簡單的場景,可以按照如下這樣使用那槽。
- 在簡單的循環(huán)中建議使用int悼沿。
- 成員/靜態(tài)/全局變量建議使用NSIntenger。
- 對較大范圍有強制要求時可以使用int64_t骚灸。
- 儲存顏色可以使用uint8_t糟趾。
除此之外,整型數(shù)還有很多種類型甚牲,他們都是根據(jù)C語言的基礎(chǔ)數(shù)據(jù)類型進(jìn)行重定義义郑。使用時可以根據(jù)代碼找到最終的數(shù)據(jù)類型和可能存在宏判斷結(jié)合 short、int丈钙、long非驮、long long 在32位和64位系統(tǒng)下大小的區(qū)別來推斷他們等價的類型。
博客:xuyafei.cn
簡書:jianshu.com/users/2555924d8c6e
微博:weibo.com/xuyafei86
Github:github.com/xiaofei86
參考資料
- [StackOverflow] Definition of int64_t
- [StackOverflow] How should I declare a long in Objective-C? Is NSInteger appropriate?
- [StackOverflow] What is Generic iOS Device in Xcode 7.1 run destinations list?
- [StackOverflow] In Cocoa do you prefer NSInteger or int, and why?
- [StackOverflow] When to use NSInteger vs. int