iOS工程如何支持64-bit

 

蘋果在2014年10月20號發佈了一條消息:從明年的二月一號開始,提交到App Store的應用必須支持64-bit。詳細消息地址爲:https://developer.apple.com/news/?id=10202014ahtml

那們咱們應該如何開始着手讓本身的App支持64-Bit呢?ios

基本知識編程

從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 位,意味着什麼網絡

  • 1.Xcode 5.0.1開始支持編譯32-bit和64-bit的Binary數據結構

  • 2.同時支持32-bit和64-bit,咱們須要選擇的minimum deployment target爲 iOS 5.1.1架構

  • 3.64-bit的Binary必須運行在支持64-bit的CPU上,而且最小的OS版本要求是 7.0.3app

關於Xcode 「Build Setting」中的Architectures參數問題ide

  • 1.Architectures:你想支持的指令集。(支持指令集是經過編譯生成對應的二進制數據包實現的,若是支持的指令集數目有多個,就會編譯出包含多個指令集代碼的數據包,形成最終編譯的包很大。)測試

  • 2.Valid architectures:即將編譯的指令集。(Valid architectures 和 Architecture兩個集合的交集爲最終編譯生成的版本)ui

  • 3.Build Active Architecture Only:是否只編譯當前設備適用的指令集(若是這個參數設爲YES,使用iPhone 6調試,那麼最終生成的一個支持ARM64指令集的Binary。通常在DEBUG模式下設爲YES,RELEASE設爲NO)

關於指令集以下參考:

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。

讓App支持32-bit和64-bit基本步驟

1.確保Xcode版本號>=5.0.1

2.更新project settings, minimum deployment target >= 5.1.1

3.改變Architectures爲 Standard architectures(include 64-bit)

4.運行測試代碼,解決編譯warnings and errors,對照本文檔或者官方文檔 64-Bit Transition Guide for Cocoa Touch對相應地方作出修改。(編譯器不能告訴咱們一切)

5.在真實的64-bit機器上測試。

6.使用Instruments查看內存使用問題。

64-bit主要的變化

64-bit運行時環境和32-bit運行時環境主要有如下兩點的不一樣:

數據類型的改變

方法調用上的改變

數據類型的改變

整型數據類型的變化以下:

6af3d1aegw1elk0ftqdaxj20t00ky0wh.jpg

關於字節對齊的概念能夠參考以下連接:C語言字節對齊

浮點型類型的改變以下:

6af3d1aegw1elk0g76x94j20h606uq3g.jpg

數據類型的改變可能會爲咱們的程序帶來這些影響:

1.增長內存壓力

2.64-bit到32-bit數據之間的相互轉化

3.計算可能產生不一樣的結果

4.當把一個值從大的數據類型拷貝到小的數據類型,數據可能被截斷。(NSInteger -> int)

方法調用上的改變

基於32-bit的CPU和基於64-bit上的CPU有不一樣數量的寄存器,在方法調用上有不一樣的協議。所以32-bit和64-bit在彙編層級上是不一樣的。若是咱們在程序中不使用匯編編程,調用協議不多會遇到。

如何編寫健壯的64-bit代碼

根據上述改變,官方文檔 64-Bit Transition Guide for Cocoa Touch給出以下7步:

1.不要將長整型long賦值給整型int (64-bit上會致使數據丟失)

2.不要將指針類型pointer賦值給整型int (64-bit致使地址數據丟失)

3.留意數值計算(掩碼計算,無符號整數和有符號整數同時使用等)

4.留意對齊方法帶來的變化

5.32-bit到64-bit之間數據轉化(經過網絡傳遞的用戶數據,可能同時存在於32-bit和64-bit的環境下)

6.重寫彙編代碼

7.不要在可變參數方法和不可變參數方法以前進行強制轉化

在LLVM編譯器中,枚舉類型也能夠定義枚舉的大小。咱們在使用中,指派枚舉值到一個變量時,應該使用適當的數據類型。

不要將指針類型pointer賦值給整型int

若是咱們必定要把指針轉化爲整型,能夠把上述代碼改成:

1

2

int *d = (int *)((uintptr_t)c + 4);

查看uintptr_t定義爲 typedef unsigned long uintptr_t;

保持數據類型一致

方法使用時,入參,出參和賦值都須要注意保持數據類型一致。在iOS App中尤爲要注意如下幾個類型的正確使用:

  • long

  • NSInteger

  • CFIndex

  • size_t

在32-bit和64-bit下,fpos_t和off_t都是64 bits的數據大小,永遠不要把它們指向int整型。

1

2

3

4

5

6

7

8

9

10

11

12

13

long PerformCalculation(void);

int c = PerformCalculation(); 

long y = PerformCalculation(); 

int PerformAnotherCalculation(int input);

long i = LONG_MAX;

int x = PerformCalculation(i); 

int ReturnMax()

{

    return LONG_MAX; 

}

Cocoa中常見的數據類型轉化問題

NSInteger : 在32-bit和64-bit下有分別的定義:

1

2

3

4

5

    #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是同樣大的,下面的例子在使用中就須要注意:

1.使用NSNumber對象轉化時

2.使用NSCoder編解碼的時候,若是在64-bit設備下對NSInteger編碼,在32-bit設備下對NSInteger解碼。解碼時若是值的大小超過了32-bit,這個時候就會出現異常

3.Famework中使用NSInteger定義的一些常量

CGFloat: 和NSInteger同樣有不一樣的定義

1

2

3

4

5

6

7

typedef CGFLOAT_TYPE CGFloat;

#if defined(__LP64__) && __LP64__

# define CGFLOAT_TYPE double

#else

# define CGFLOAT_TYPE float

#endif

下面給出錯誤示範:

1

2

3

4

5

CGFloat value = 200.0;

CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &value); 

CGFloat value = 200.0;

CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &value); 

整型數值計算問題

關於C語言的符號位擴展可參考資料爲:符號位擴展

咱們直接來看例子:

1

2

3

4

5

6

7

8

9

    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下,咱們知道數據是一個固定的大小或者一個特定的變量擁有一個有限的取值範圍。這個時候,咱們應該選擇特定的類型以免浪費內存。

類型以下:

32.jpg

永遠不要使用malloc去爲變量申請特定內存的大小,改成使用sizeof來獲取變量或者結構體的大小。

另外咱們還須要注意修改格式化字符串來同時支持32-bit和64-bit。

33.jpg

當心處理方法和方法指針

1

2

3

4

5

6

7

8

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上,是使用徹底不一樣的協議來編譯的。

若是在代碼中傳遞方法指針,應該保證方法調用的協議是一致的。永遠不要將一個可變參數的方法轉化成固定參數的方法。

1

2

3

4

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 "{}" \;

相關文章
相關標籤/搜索