IOS系統從07年出來,到現在也有6年了,每年發布一次到兩次新的設備,從iPhone1,iPhone2 ... iPhone4s再到最新的iPhone5。硬件在升級的過程中CPU的架構也可能發生變化,如最早的armv6,armv7再帶最新的iPhone5所以用的armv7s。同時伴隨每一次硬件升級,系統都會做一次大版本的升級,目前最新的版本已經到了6.x了。
每次系統升級的時候,總有一些用戶會因為各種原因不願或者不能升級到最新的系統。這就意味着,如果我們的程序要讓盡可能多的人使用,就得支持盡可能多的硬件架構及系統版本。如果我們寫的程序和硬件打交道比較少的話,要做的事也就是配置一下工程選項中的Architectures,在其中添加上我們要支持的硬件架構就好了。然而每次系統的升級都會伴隨這很多新的API,Framework的引入,以及部分老的API的廢棄。也就說我們要支持的系統版本越多,工作量也就越大,所以我們通常會根據工作量和系統版本的分布情況做出取舍。現在AppStore上基本上的程序一般最多也就支持IOS 4.3的系統了吧,我做的上個項目PowerCam記得也是從4.0版本開始支持。
下面我們分別從兩個方面討論一下:
一、支持多設備類型
不同的設備CPU指令集可能是不同的,這也就導致了其支持的框架可能不同。例如iPhone3G及之前版本的設備的CPU只支持armv6的指令集,導致了這些設備只支持OpenGL ES 1.1版本,所有iPad及iPhone3GS之后的所有設備都采用armv7及armv7s(iPhone5,iPad4采用)指令集,因此既可以支持OpenGL ES 1.1和2.0。

二、支持多系統版本
Xcode在工程的編譯選項中,我們可以看到兩個選項:Base SDK和Deployment Target,第一個選項是用來設置我們項目是基於哪個版本的SDK開發,第二選項是用來設置我們的項目最低可以部署到哪一個系統運行。
Xcode新建的工程中通常都會采用最用的SDK以及支持到最新的版本。因為我們要既支持新的版本,同事兼顧的老的版本,為了能夠利用上新版本中新的特性,所以最好選擇最新的SDK,這一項通常不用改,維持原樣就好。如果要支持叫早的系統版本,則需要修改Deployment Target選項。如下圖所示,我們設置項目需要最老的版本為IOS 4.3。
到這兒就設置完了,Xcode會根據我們的設置進行編譯打包。在這里我們討論一下Xcode是如何使用了最新版本的SDK,卻可以支持到較老的系統版本呢?查閱文檔SDK Compatibility Guide發現,在程序打包的時候,Xcode只是將一些導出符號打到我們的程序包中,具體到運行時會根據實際的設備的系統版本進行匹配。文檔中有一個圖可以解釋Base SDK和Deployment Target之間是如何運作的,如下圖:
該圖是以Mac OS工程為例的,不過原理是一樣的。意思是對於我們支持部署的版本之前的所有API,我們都是可以無條件使用的(當前在新的版本中最好使用新的API),在我們支持部署的版本和我們開發所基於的SDK版本之間的API,能否使用取決於我們的APP具體運行的系統版本。
在系統版本升級的過程中,通常會添加一些新的API和Framework,也會廢棄掉一些舊的效率不高的API。這些廢棄的API雖然不能立馬變得不可用,但是不保證在后面的版本中會繼續支持,所以為了我們的程序能夠在新的系統中更可靠,高效的運行,我們應該盡量使用新的API。
無論是在新的系統版本中調用已經不支持了的API,還是在舊的系統版本中調用新的系統版本中才引入的API都會導致我們的程序Crush。因此我們在調用那些我們部署的版本以后添加的API或者Framework時需要進行判斷當前程序所運行的環境是否支持。
下面我們從幾個方面討論:
1) 判斷一個類是否可用
在IOS 4.2以后我們可以通過class這個類方法來判斷一個類在當前運行時是否可用,代碼如下:
if ([UICollectionView class]) { // 6.0以后可以使用 } else { // 之前的版本,需要使用可替代的技術實現 }
在Mac OS則需要使用NSClassFromString來判斷一個類是否可用。
2) 判斷一個方法是否可用
NSObject類有一個方法instancesRespondToSelector可以用來判斷一個類的實例是否響應指定的方法,如果要判斷一個類是否響應一個類方法則可以使用respondToSeletor方法。代碼如下:
if ([UIViewController instancesRespondToSelector:@selector(presentViewController:animated:completion:)]) { // 5.0以后支持 } else { // 不支持該方法 }
3) 判斷一個函數是否可用
我們知道C語言中每個函數名都代表着這個函數的地址,因此我們可以通過判斷該函數名字是否NULL來判斷支持該函數。
if (CGColorCreateCopyWithAlpha != NULL) { // 支持該函數 } else { // 不支持該函數 }
4) 判斷一個extern 變量或者Notification名是否可用
extern變量和Notification名其實都是一個變量,我們只需要判斷它的地址是否NULL即可,代碼如下:
if (&MPMoviePlayerReadyForDisplayDidChangeNotification != NULL) { // 6.0以后存在該通知 } else { // 不存在該通知 }
該文檔中還講到如果你想針對不同的SDK版本進行條件編譯,可以采用宏來實現,感興趣的同學可以自己看看。
至此,多版本多設備類型支持注意事項就介紹完了,希望對大家能有幫助。
參考文檔: SDK Compatibility Guide
注:轉載請注明出處!歡迎大家加我QQ 1592232964,一起討論共同進步。