那們咱們應該如何開始着手讓本身的App支持64-Bit呢?html
從iPhone 5S的A7 CPU開始到剛剛發佈的iPhone 6(A8 CPU)都已經支持64-bit ARM 架構。關於64-bit的介紹詳見維基百科。知乎上有不少關於蘋果使用A7,A8芯片的討論,能夠參考 iPhone 6 的 Apple A8 芯片對比 Apple A7 提高明顯嗎?, iPhone 5s 配備的 A7 處理器是 64 位,意味着什麼?ios
關於指令集以下參考:編程
ARMv8/ARM64: iPhone 6(Plus), iPhone 5s, iPad Air(2), Retina iPad Mini(2,3) ARMv7s: iPhone 5, iPhone 5c, iPad 4 ARMv7: iPhone 3GS, iPhone 4, iPhone 4S, iPod 3G/4G/5G, iPad, iPad 2, iPad 3, iPad Mini ARMv6: iPhone, iPhone 3G, iPod 1G/2G
對於支持64-bit,咱們能夠設置Architectures爲 Standard architectures,在最新的Xcode 6上,它包括 armv7和arm64。網絡
64-bit運行時環境和32-bit運行時環境主要有如下兩點的不一樣:數據結構
整型數據類型的變化以下:架構
關於字節對齊的概念能夠參考以下連接:http://blog.csdn.net/21aspnet/article/details/6729724#commentsapp
浮點型類型的改變以下:ide
數據類型的改變可能會爲咱們的程序帶來這些影響:測試
基於32-bit的CPU和基於64-bit上的CPU有不一樣數量的寄存器,在方法調用上有不一樣的協議。所以32-bit和64-bit在彙編層級上是不一樣的。若是咱們在程序中不使用匯編編程,調用協議不多會遇到。ui
根據上述改變,官方文檔 64-Bit Transition Guide for Cocoa Touch給出以下7步:
在LLVM編譯器中,枚舉類型也能夠定義枚舉的大小。咱們在使用中,指派枚舉值到一個變量時,應該使用適當的數據類型。
int a = 5; int *c = &a; /* 32-bit下正常,64-bit下錯誤。最新的Xcode6.0編譯提示警告:'Cast to int* for smaller integer type int'*/ int *d = (int *)((int)c + 4); /* 正確, 指針能夠直接增長*/ int *d = c + 1;
若是咱們必定要把指針轉化爲整型,能夠把上述代碼改成:
/* 32-bit和64-bit都正常。*/ int *d = (int *)((uintptr_t)c + 4);
查看uintptr_t定義爲 typedef unsigned long uintptr_t;
方法使用時,入參,出參和賦值都須要注意保持數據類型一致。在iOS App中尤爲要注意如下幾個類型的正確使用:
在32-bit和64-bit下,fpos_t和off_t都是64 bits的數據大小,永遠不要把它們指向int整型。
long PerformCalculation(void); int c = PerformCalculation(); // 錯誤 64-bit上數據將被截取 long y = PerformCalculation(); // 正確 int PerformAnotherCalculation(int input); long i = LONG_MAX; int x = PerformCalculation(i); // 錯誤 int ReturnMax() { return LONG_MAX; // 錯誤 }
NSInteger : 在32-bit和64-bit下有分別的定義:
#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64 typedef long NSInteger; #else typedef int NSInteger; #endif
咱們永遠不該該假設NSInteger和int是同樣大的,下面的例子在使用中就須要注意:
CGFloat: 和NSInteger同樣有不一樣的定義
typedef CGFLOAT_TYPE CGFloat; #if defined(__LP64__) && __LP64__ # define CGFLOAT_TYPE double #else # define CGFLOAT_TYPE float #endif
下面給出錯誤示範:
CGFloat value = 200.0; CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &value); //64-bit下出現錯誤 CGFloat value = 200.0; CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &value); //正確
關於C語言的符號位擴展可參考資料爲:http://blog.163.com/shi_shun/blog/static/237078492010651063936/
咱們直接來看例子:
int a = -2; unsigned int b = 1; long c = a + b; long long d = c; printf("%lld\n", d);
問題:這段代碼在32-bit下運行結果符合咱們的預期,輸出爲 -1(0xffffffff)。在64-bit下運行結果爲:4294967295 (0x00000000ffffffff)。
緣由:一個有符號的值和一個一樣精度的無符號的值相加結果是無符號的。這個無符號的結果被轉換到更高精度的數值上時採用零擴展。
解決方案:把變量b換成長整型long
C99提供了內置的數據類型保證了一致的數據大小,即便底層的硬件結構不一樣。在某些case下,咱們知道數據是一個固定的大小或者一個特定的變量擁有一個有限的取值範圍。這個時候,咱們應該選擇特定的類型以免浪費內存。
類型以下:
永遠不要使用malloc去爲變量申請特定內存的大小,改成使用sizeof來獲取變量或者結構體的大小。
另外咱們還須要注意修改格式化字符串來同時支持32-bit和64-bit。
int fixedFunction(int a, int b); int variadicFunction(int a, ...); int main { int value2 = fixedFunction(5,5); int value1 = variadicFunction(5,5); }
上述兩個方法中,在32-bit下使用相同的指令讀取參數的數據,可是在64-bit上,是使用徹底不一樣的協議來編譯的。
若是在代碼中傳遞方法指針,應該保證方法調用的協議是一致的。永遠不要將一個可變參數的方法轉化成固定參數的方法。
int MyFunction(int a, int b, ...); int (*action)(int, int, int) = (int (*)(int, int, int)) MyFunction; action(1,2,3); // 錯誤示範
上述錯誤的寫法,編譯器是不會提示警告或者錯誤的,而且在模擬器中也不會暴露出問題來。在發佈本身的App前,必定記得要使用真機去測試。
在支持64-bit過程當中,應該按照Apple文檔中提供的7個步驟完整檢查項目工程。若是工程中涉及到大量的C或者C++代碼,在支持64-bit中要更加謹慎。
寫完這篇筆記後,我以爲須要重溫一下C的基礎知識。XD,順便祈禱項目中的第三方庫趕忙更新支持64-bit,阿彌陀佛。
ps: 找出不支持arm64的靜態庫 find . -name *.a -exec lipo -info "{}" \;