iOS應用如何實現64位的支援
蘋果在2014年10月20號釋出了一條訊息:從明年的二月一號開始,提交到App Store的應用必須支援64-bit。詳細訊息地址為:https://developer.apple.com/news/?id=10202014a
那們我們應該如何開始著手讓自己的App支援64-Bit呢?
基本知識
從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 位,意味著什麼?
- Xcode 5.0.1開始支援編譯32-bit和64-bit的Binary
- 同時支援32-bit和64-bit,我們需要選擇的minimum deployment target為 iOS 5.1.1
- 64-bit的Binary必須執行在支援64-bit的CPU上,並且最小的OS版本要求是 7.0.3
關於Xcode “Build Setting”中的Architectures引數問題
- Architectures:你想支援的指令集。(支援指令集是通過編譯生成對應的二進位制資料包實現的,如果支援的指令集數目有多個,就會編譯出包含多個指令集程式碼的資料包,造成最終編譯的包很大。)
- Valid architectures:即將編譯的指令集。(Valid architectures 和 Architecture兩個集合的交集為最終編譯生成的版本)
- 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基本步驟
- 確保Xcode版本號>=5.0.1
- 更新project settings, minimum deployment target >= 5.1.1
- 改變Architectures為 Standard architectures(include 64-bit)
- 執行測試程式碼,解決編譯warnings and errors,對照本文件或者官方文件 64-Bit Transition Guide for Cocoa Touch對相應地方做出修改。(編譯器不能告訴我們一切)
- 在真實的64-bit機器上測試
- 使用Instruments檢視記憶體使用問題
64-bit主要的變化
64-bit執行時環境和32-bit執行時環境主要有以下兩點的不同:
- 資料型別的改變
- 方法呼叫上的改變
資料型別的改變
整型資料型別的變化如下:
關於位元組對齊的概念可以參考如下連結:http://blog.csdn.net/21aspnet/article/details/6729724#comments
浮點型型別的改變如下:
資料型別的改變可能會為我們的程式帶來這些影響:
- 增加記憶體壓力
- 64-bit到32-bit資料之間的相互轉化
- 計算可能產生不同的結果
- 當把一個值從大的資料型別拷貝到小的資料型別,資料可能被截斷。(NSInteger -> int)
方法呼叫上的改變
基於32-bit的CPU和基於64-bit上的CPU有不同數量的暫存器,在方法呼叫上有不同的協議。因此32-bit和64-bit在彙編層級上是不同的。如果我們在程式中不使用匯編程式設計,呼叫協議很少會遇到。
如何編寫健壯的64-bit程式碼
根據上述改變,官方文件 64-Bit Transition Guide for Cocoa Touch給出如下7步:
- 不要將長整型long賦值給整型int (64-bit上會導致資料丟失)
- 不要將指標型別pointer賦值給整型int (64-bit導致地址資料丟失)
- 留意數值計算(掩碼計算,無符號整數和有符號整數同時使用等)
- 留意對齊方法帶來的變化
- 32-bit到64-bit之間資料轉化(通過網路傳遞的使用者資料,可能同時存在於32-bit和64-bit的環境下)
- 重寫彙編程式碼
- 不要在可變引數方法和不可變引數方法之前進行強制轉化
在LLVM編譯器中,列舉型別也可以定義列舉的大小。我們在使用中,指派列舉值到一個變數時,應該使用適當的資料型別。
不要將指標型別pointer賦值給整型int
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中尤其要注意以下幾個型別的正確使用:
- long
- NSInteger
- CFIndex
- size_t
在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; // 錯誤 }
Cocoa中常見的資料型別轉化問題
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是一樣大的,下面的例子在使用中就需要注意:
- 使用NSNumber物件轉化時
- 使用NSCoder編解碼的時候,如果在64-bit裝置下對NSInteger編碼,在32-bit裝置下對NSInteger解碼。解碼時如果值的大小超過了32-bit,這個時候就會出現異常
- Famework中使用NSInteger定義的一些常量
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中要更加謹慎。
相關文章
- iOS–KVO的實現原理與具體應用iOS
- iOS應用程式的脫殼實現原理淺析iOS
- Thunk程式的實現原理以及在iOS中的應用iOS
- 實現 WebSphere Application Server 上應用程式對 OSGi 的支援WebAPPServer
- Thunk程式的實現原理以及在iOS中的應用(二)iOS
- eXo 如何實現中文支援?
- CRM的行程支援是如何實現的?行程
- 如何用istio實現應用的灰度釋出
- 如何實現 Android 應用的持續部署?Android
- Blazor Web 應用如何實現Auto模式BlazorWeb模式
- 如何實現 iOS 中的 Associated ObjectiOSObject
- 給iOS應用配置支援通用連結訪問iOS
- OpenKruise 如何實現應用的可用性防護?UI
- 陣列有哪些方法支援響應式更新的?底層原理如何實現?陣列
- UWP 透過 .NET 9 和Native AOT 的支援實現 UWP 應用的現代化
- ThinkJS 3.0 如何實現對 TypeScript 的支援JSTypeScript
- Spring boot應用如何支援httpsSpring BootHTTP
- iOS 11開發教程(二十二)iOS11應用檢視實現按鈕的響應(2)iOS
- iOS應用支援IPV6,就那點事兒iOS
- iOS 應用架構現狀分析iOS應用架構
- 棧的應用和實現
- 如何讓你的SAP CRM應用支援附件上傳
- 蘋果ios簽名:iPhone9新應用必須支援ios13蘋果iOSiPhone
- 如何實現7*24小時慢直播應用?
- 如何在直播應用中實現多人KTV?
- 請教這個應用技術如何實現?
- iOS11開發教程(二十三)iOS11應用檢視實現按鈕的響應(3)iOS
- [Flutter翻譯]Canonical通過Flutter實現對Linux桌面應用的支援FlutterLinux
- 用“雲”實現SaaS應用
- iOS版應用支援IPV6,就那點事兒iOS
- 批次上傳iOS應用程式截圖的實用技巧iOS
- 如何在我的應用啟動介面實現「開屏廣告」?
- 如何用 Java 實現 Web 應用中的定時任務?JavaWeb
- 說說你對堆的理解?如何實現?應用場景?
- App Promo:調查顯示58%的iOS應用未能實現盈虧平衡APPiOS
- 如何在移動應用中實現AI畫圖?AI
- 如何使用Android原生介面,實現“應用雙開”Android
- 如何用 Vue.js 實現一個建站應用Vue.js