天天看點

《編寫高品質代碼:改善Objective-C程式的61個建議》——建議11:謹記相容32位和64位環境下代碼編寫事項

本節書摘來自華章出版社《編寫高品質代碼:改善objective-c程式的61個建議》一 書中的第2章,作者:劉一道,更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視。

在ios 7版本出現之前,應用程式主要都是基于32位的ios運作環境設計的,很少會考慮到要相容64位的ios運作環境。現在64位的ios運作環境已經出現了。這個時候,在編寫應用程式的時候,就不得不考慮了如何確定自己寫的應用程式,既能在ios的32位環境下運作又能在64位的環境下運作。

下面就編寫相容ios 32位和64位運作環境的應用程式容易犯的錯誤,進行逐一介紹,希望能對各位有所幫助。

不要将長整型資料賦予整型

在許多導緻程式設計錯誤産生因素之中,最為典型的因素莫過于在應用程式的整個代碼中,不能使用一貫的資料類型,導緻編譯應用程式代碼時候,産生大量的警告提醒資訊。

故此,當調用函數時,要確定接收到的結果與該函數傳回的變量的類型相比對。與接收變量相比,如果傳回類型是一個較大的整數,那麼該值将會被截斷。

下面的代碼示例就表現出了此種錯誤,配置設定給一個變量時卻截斷一個傳回值。在此代碼示例中performcalculation 函數将傳回一個長整型。在 32 位運作時中,int 和 long 都是 32 位,即使代碼不正确,但也能確定配置設定為int類型能有效工作。在 64 位運作時中,結果的高32位被配置設定時,将被丢掉。要保證不出現資料丢失,就應将結果賦給一個長整數 ,確定代碼在32位和64位的運作時環境中都能有效運作。

當作為一個參數傳遞值時,也會出現與上邊一樣的問題。例如,在下面的示例代碼中64位運作時執行的輸入參數時被截斷。

在下面的代碼示例中,在64位運作時中傳回值也被截斷,因為該函數的傳回類型超出傳回值的範圍。

在上面的這些示例中,都是假定int 和 long 是完全相同的代碼,即相同的資料類型,但是ansi c 标準不能確定假設的這些情況都成立。當應用程式運作在64位環境中時,上面的代碼就會出現明顯錯誤。在預設編譯環境下,編譯器會自動啟用32位和64位校驗機制,一旦一個值被截斷,在大部分情況下,編譯器将會自動抛出警告。如果編譯器沒有啟用32位和64位校驗機制,這時候應該在編譯器選項中明确啟用它,或者同時選擇轉化選項,這樣就能利用編譯器的校驗機制,發現更多更詳細的潛在錯誤。

在cocoa touch的應用程式中,查找以下的整數類型并確定正确地使用它們:

在這兩個運作時環境中的 fpos_t 和 off_t 的類型都是 64 位的,是以從來沒有将它們配置設定給一個 int 類型。

善用nsinteger來處理32位和64位之間的轉換

在編寫應用程式時,可以在代碼中多用nsinteger來處理數字類型的變量,因為nsinteger可以與ios不同的運作環境相容。

無論是在運作32位運作環境中,還是在運作在64位環境中,nsinteger的類型的應用貫穿于整個cocoa touch。其在 32 位運作時中是32 位整數,其在64 位運作時中是 64 位整數。是以,當從一個架構的方法接收資訊時,它采用一個nsinteger的類型,是以務必使用nsinteger的類型來儲存結果。

永遠不要假設nsinteger的類型是一個大小相同的int類型,這裡有幾個關鍵例子來看看:

轉換成nsnumber對象或轉換nsnumber對象為其他。

編碼和解碼資料裡,使用的是 nscoder 類。尤其是,編碼 nsinteger在64 位的裝置上,但将它解碼在 32 位的裝置上,如果值超過一個 32 位整數的範圍,解碼方法将會引發異常。

使用 nsinteger 作為架構中定義的常量。特别值得注意的是nsnotfound 常數。它的價值是大于一個int類型的最大範圍,是以在的應用程式中截斷其價值往往會導緻錯誤的出現。

在64位的代碼中,cgfloat的大小改變了,gfloat類型變為一個64位單精度數。作為nsinteger的類型,不能想當然地把cgfloat認為是一個單精度或雙精度數類型。是以,要使用一緻的cgfloat。下面的示例就是使用core foundation來建立 cfnumber的。

建立資料結構要注意固定大小和對齊

當資料在32位和64位版本的應用程式之間共享時,就有必要確定建立相容32位和64位的資料結構是完全相同的。當資料存儲在一個檔案或在一個傳輸網絡的裝置中時,其運作環境可能是相對立的。例如,使用者把資料備份存儲在32位的裝置中,卻在64位的裝置中進行資料恢複,環境的差異性,在一定的程度上,會影響資料的正常使用,故此,對于這樣的資料互操作存在的問題,必須要找到解決的方法。

(1)使用明确的整數資料類型

不管底層的硬體結構,c99标準提供了内置的資料類型都是特定大小的。當資料必須是一個固定大小時,或者當知道一個特定的變量有一個有限的可能值範圍時,這個時候就應該考慮使用這些資料類型。通過選擇适當的資料類型,會得到一個固定寬度的類型,可以存儲在記憶體中,也避免配置設定一個變量時浪費記憶體,其範圍遠大于需要。

表 2-1列出每個 c99 類型和範圍的允許值。

《編寫高品質代碼:改善Objective-C程式的61個建議》——建議11:謹記相容32位和64位環境下代碼編寫事項

(2)對齊64位整數類型時要小心

在64位運作時,所有的64位整數類型的變化從4位元組到8位元組對齊。即使明确指定每個整數類型,這兩種結構仍然可能是不相同的兩個運作時。在代碼清單2-1中,對齊改變,即使該字段聲明明确的整數類型。

代碼清單2-1 對齊的64位整數結構

當使用32位的編譯器編譯此代碼時,字段欄(field bar)是從結構起始開始的12位元組;當使用 64 位編譯器編譯該代碼時,字段欄(field bar)是從結構起始的16位元組開始的。在foo2中填充4位元組,就可以確定foo2的欄(bar)具有8位元組,進而實作邊界對齊。

如果定義新的資料結構,首先組織的元素具有最大對齊值,最後的是最小的元素。這個結構組織消除正是大多數的填充位元組需要的。如果正在使用現有的結構,其中包括未對齊的 64 位整數,可以使用 pragma 來強制其正确對齊。代碼清單2-2給出了相同的資料結構,但這裡的結構是被迫使用32位對齊規則的。

代碼清單2-2 使用的pragma控制對齊

 隻有在必要時才使用此選項,主要因為通路未對齊的資料,易造成性能上損失。故此要想確定自己的32位版本的應用程式中資料結構,能具有向後相容性,就有很必要使用此選項。

選擇一種緊湊的資料表示形式

在編寫應用程式代碼時,選擇一種恰當的資料結構形式,就可以比較好地表示資料。例如,使用下面的資料結構存儲月曆日期:

這種結構的長度為24位元組;在64位運作時,它需要48位元組,隻為一個日期!一個更緊湊的表示方式是以秒數來存儲某一特定時間。必要時,将此緊湊的表示形式轉換為月曆的日期和時間。

對于對齊的資料結構,編譯器有時會向其中添加填充物,例如:

這種結構包括 14 位元組的資料,但由于填充,它占用的 24 位元組的空間。更好的設計方式是對字段進行從大到小的排序來對齊。

 要點

(1)不要将長整型資料賦予整型。

(2)利用用nsinteger來處理32位和64位之間的轉換。

(3)建立資料結構要注意固定大小和對齊。