【翻譯】Cocoa Touch 64位轉(zhuǎn)移指南

原文鏈接:64-Bit Transition Guide for Cocoa Touch

將你的App轉(zhuǎn)換成64位的二進制文件

總的來說巨朦,下面列出了創(chuàng)建一個同時適用32位和64位運行時環(huán)境的app的步驟:

  1. 安裝最新版的Xcode
  2. 打開你的工程嫉你。Xcode會提示你去更新你的工程名段。這使得編譯64位的app的時候會報新的warning和error。
  3. 修改你的工程設置以支持iOS 5.1.1或更高版本发框。目標iOS版本低于5.1.1時不支持編譯64位工程躺彬。
  4. 把工程的編譯設置中的體系結(jié)構(gòu)置為“Standard Architectures (including 64-bit).”
  5. 更新app以支持64位運行時環(huán)境。新的編譯器報的warning和error會幫助你實現(xiàn)這個步驟梅惯。然而它不能幫你搞定所有的事宪拥,本文檔能在調(diào)查你的代碼的時候幫助你。
  6. 在真實的64位的硬件上測試你的app铣减。iOS模擬器在開發(fā)時很有用她君,但有些變化(例如函數(shù)調(diào)用的規(guī)則)只能在真機運行時看出來。
  7. 使用Instruments來測試app的內(nèi)存表現(xiàn)葫哗。
  8. 提交一個包含32位和64位兩種體系結(jié)構(gòu)的app以供審核缔刹。
    本章剩余部分描述了一些把app轉(zhuǎn)移到64位運行時環(huán)境常見的問題。這些段落能幫你仔細研究你的代碼劣针。

不要把指針強轉(zhuǎn)為整型數(shù)

總有些強轉(zhuǎn)指針到整型數(shù)的理由校镐。當一致地用指針類型時,你可以保證所有的變量都足夠大到放得下一個地址捺典。
例如鸟廓,代碼清單2-1中的代碼把一個指針轉(zhuǎn)成了一個int,因為它想對地址進行算術(shù)運算。在32位運行時中引谜,這段代碼是有效的牍陌,因為一個int和一個指針大小相同。然而在64位運行時中员咽,指針比int要大呐赡,所以這個賦值操作丟失了指針的一部分數(shù)據(jù)。正確的做法是直接遞增這個指針骏融,因為指針會隨著本機的內(nèi)存地址的寬度而改變。

代碼清單2-1 把指針強轉(zhuǎn)為int

int *c = something passed in as an argument....
int *d = (int *)((int)c + 4);  // 錯誤萌狂。
int *d = c + 1;                // 正確档玻!

如果你一定要這樣做,請使用uintptr_t類型來避免截斷茫藏。通過整數(shù)運算來改變指針的值误趴,然后在轉(zhuǎn)回指針會違反基本類型別名的規(guī)則,請注意务傲。這也會導致編譯器的一些意外的行為凉当,以及訪問不對齊的指針時處理器錯誤。

數(shù)據(jù)類型前后一致

代碼中數(shù)據(jù)類型前后不一致會導致很多常見的程序錯誤售葡。雖然編譯器會對這樣的行為給出警告看杭,但我們還是有必要來看一些這種問題來幫助我們在代碼中識別這種不一致。
當調(diào)用函數(shù)時挟伙,要保持接受返回結(jié)果的變量和函數(shù)返回值類型保持一致楼雹。如果返回值類型比接受返回結(jié)果的變量更長,那么這個值會被截斷尖阔。代碼清單2-2展示了這種問題的一個例子贮缅。函數(shù)PerformCalculation的返回值是long型。在32位環(huán)境下介却,intlong都是32位的谴供,所以把返回值賦值給一個int變量是不會出錯的,雖然這段代碼是不正確的齿坷。在64位環(huán)境下桂肌,這樣賦值會導致返回值的高32位丟失。所以這個返回值應該被賦予一個long型數(shù)胃夏,這樣在兩種環(huán)境中都是正確的轴或。

代碼清單2-2 返回值賦值時發(fā)生截斷

long PerformCalculation(void);

  int x = PerformCalculation();  // 錯誤
  long y = PerformCalculation();  // 正確!

同樣的錯誤會在傳參的時候發(fā)生仰禀。例如照雁,在代碼清單2-3中,在64位環(huán)境下入?yún)唤財唷?/p>

代碼清單2-3 傳入?yún)?shù)時發(fā)生截斷

int PerformAnotherCalculation(int input);
  long i = LONG_MAX;
  int x = PerformAnotherCalculation(i);

在代碼清單2-4中,在64位環(huán)境下返回值同樣會被截斷饺蚊,因為返回的值超過了函數(shù)返回值類型的范圍萍诱。

代碼清單2-4 返回值發(fā)生截斷

int ReturnMax()
{
  return LONG_MAX;
}

這些例子都假定intlong是等長的。ANSI C 標準沒有這個假設污呼,并且這樣在64位環(huán)境中是完全錯誤的裕坊。如果你的工程是最新的,編譯器的-Wshorten-64-to-32選項是默認自動開啟的燕酷,所以編譯器在大多數(shù)情況下會自動發(fā)出關(guān)于截斷的警告籍凝。如果你的工程不是最新的,你應該明確地啟用這個編譯器選項苗缩《伲或者你也可以包含-Wconversion選項,這雖然有冗余但是會找出更多潛在的錯誤酱讶。
在Cocoa Touch app中退盯,找到使用下列整型數(shù)的地方,并保證他們使用正確:

  • long
  • NSInteger
  • CFIndex
  • size_tsizeof結(jié)果的真正類型)
    在32位和64位這兩種環(huán)境中泻肯,fpos_toff_t類型長度都是64位渊迁,所以不要把它們賦值給int變量。

枚舉也是有類型的

在LLVM編譯器中灶挟,枚舉類型可以指定枚舉值的大小琉朽。這意味著有時候枚舉類型的大小會超過你的預期。解決方案和上面類似稚铣,不要假定枚舉值數(shù)據(jù)類型的大小漓骚。而應該將枚舉值賦給合適類型的變量。

Cocoa Touch中常見的類型轉(zhuǎn)換的問題

Cocoa Touch榛泛,特別是Core Foundation和Foundation蝌蹂,除了上述的問題還有額外的需要關(guān)注的情況,因為它們提供了序列化C數(shù)據(jù)和在Objective-C對象中拿到C數(shù)據(jù)的方式曹锨。
NSInteger在64位中的長度和32位中的不同孤个。NSInteger的使用范圍遍布整個Cocoa Touch,它在32位環(huán)境是32位整數(shù)沛简,在64位環(huán)境是64位整數(shù)齐鲤。所以當我們從框架中的一個方法獲得NSInteger的值的時候,應該使用NSInteger的變量來保存這個結(jié)果椒楣。
即使你永遠不要假定NSIntegerint一樣長给郊,我們也需要查找一下以下一些極端情況的例子:

  • 轉(zhuǎn)到NSNumber對象或者從NSNumber對象轉(zhuǎn)來時。
  • NSCoder類編碼或解碼一段數(shù)據(jù)時捧灰。特別地淆九,如果你在64位環(huán)境下編碼一個NSInteger然后在32位環(huán)境下解碼,如果結(jié)果超過了32位整數(shù)的范圍,解碼的方法會拋出一個異常炭庙。你大概會需要使用一種嚴格的整數(shù)類型來替代它(參見使用嚴格的整數(shù)類型
  • 當使用框架內(nèi)定義的NSInteger常量時饲窿。特別注意的是常量NSNotFound。在64位環(huán)境中這個值比int的最大值更大焕蹄,所以如果這個值發(fā)生截斷通常會帶來錯誤逾雄。
    CGFloat在64位中的長度和32位中的不同。CGFloat類型變?yōu)?4位的浮點數(shù)腻脏。和NSInteger一樣鸦泳,你不能假定CGFloatfloat還是double。所以一致地使用CGFloat永品。代碼清單2-5展示了一個例子辽故,使用Core Foundation來創(chuàng)建了一個CFNumber變量。但是這段代碼假定CGFloatfloat一樣長腐碱,這是不對的。

代碼清單2-5 一致使用CGFloat

// 錯誤掉弛。
CGFloat value = 200.0;
CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &value);

// 正確症见!
CGFloat value = 200.0;
CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &value);

小心地計算整型數(shù)

雖然截斷是最常見的問題,你還可能遇到其他與整型數(shù)變化相關(guān)的問題殃饿。這一節(jié)包含了一些額外的信息可以幫助你更新代碼谋作。

C和C衍生語言的符號擴展規(guī)則

C以及類似的語言使用一套符號擴展規(guī)則,它決定了當把值賦到一個更寬的變量時是否把整數(shù)的最高位當做符號位乎芳。這套規(guī)則如下:

  1. 當賦值給更寬的類型時遵蚜,無符號值高位補0(而不是補符號位)。
  2. 帶符號值永遠補符號位奈惑,及時結(jié)果類型是無符號的吭净。
  3. 常量(除非是有后綴的,例如0x8L)被認為是可以放得下這個值的最短類型肴甸。編譯器認為十六進制形式的字面量是int寂殉、longlong long類型的,并且有可能是signed或者unsigned原在。十進制數(shù)都被認為是signed友扰。
  4. 同樣寬度的帶符號值和無符號值的和是無符號的。
    代碼清單2-6的例子展示了這些規(guī)則意外的行為以及相應的解釋庶柿。

代碼清單2-6 符號擴展例1

int a = -2;
unsigned int b = 1;
long c = a + b;
long long d = c; // 為了格式化輸出

printf("%lld\n", d);

問題:當這段代碼運行在32位環(huán)境時村怪,結(jié)果是-10xffffffff)。當運行在64位環(huán)境時浮庐,結(jié)果是42949672950x00000000ffffffff)甚负,這結(jié)果可能不是你所期望的。
原因:為什么會這樣?首先腊敲,這兩個數(shù)相加击喂。帶符號加無符號結(jié)果是無符號的(規(guī)則4)。然后這個值賦到了一個更寬的類型的變量中碰辅。而這一步并沒有擴展符號位懂昂。
解決:為了修復這個bug并且兼容32位,需要把b轉(zhuǎn)換成long型的整數(shù)没宾。這個轉(zhuǎn)換會在加法操作之前強制把b通過無符號擴展變成64位凌彬,因此強制把那個帶符號整數(shù)(帶符號地)擴展成64位來匹配b。這樣改之后循衰,結(jié)果應該是-1了铲敛。
代碼清單2-7展示了一個相關(guān)的例子以及相應的解釋。

代碼清單2-7 符號擴展例2

unsigned short a = 1;
unsigned long b = (a << 31);
unsigned long long c = b;

printf("%llx\n", c);

問題:預期的結(jié)果(32位執(zhí)行的結(jié)果)是0x80000000会钝。64位執(zhí)行的結(jié)果卻是0xffffffff8000000伐蒋。
原因:為什么發(fā)生了符號擴展?首先當移位操作符調(diào)用的時候迁酸,變量a被隱式轉(zhuǎn)換為int先鱼。因為所有short值都包含在帶符號的int范圍內(nèi),所以轉(zhuǎn)換的結(jié)果是帶符號的奸鬓。
其次當移位完成時焙畔,結(jié)果存入了一個long型整數(shù)。因此串远,當(a << 31)代表的32位的值轉(zhuǎn)換到64位的值的時候進行了符號擴展(規(guī)則2)宏多,即使結(jié)果類型是無符號的。
解決:在移位操作之前把初始值轉(zhuǎn)成long型數(shù)澡罚。這個short值只進行一次轉(zhuǎn)換——而這次是轉(zhuǎn)換到64位類型(當編譯成64位可執(zhí)行文件時至少要到64位)伸但。

使用位操作和掩碼

當對64位的值進行位操作和使用掩碼時,你希望避免不經(jīng)意地得到32位的值留搔。下面有一些有幫助的建議砌烁。
不要假定一種數(shù)據(jù)類型有特定的長度。如果你正在遍歷一個long型數(shù)的位催式,使用LOG_BIT來確定long型數(shù)有多少位函喉。超出長度的移位的結(jié)果取決于不同的體系結(jié)構(gòu)。
如果需要的話荣月,使用取反的掩碼管呵。當對long型數(shù)使用掩碼時要謹慎,因為在32位和64位環(huán)境中long的寬度是不同的哺窄。有兩種創(chuàng)建掩碼的方法捐下,選擇哪一種取決于你希望掩碼是高位補0還是補1:

  • 如果你希望在64位環(huán)境下掩碼的高32位都是0账锹,那正常的固定寬度的掩碼是不會出錯的,因為這個掩碼會按照無符號的方式擴展到64位坷襟。
  • 如果你希望掩碼的高位都是1奸柬,對掩碼做兩次按位取反,正如代碼清單2-8展示的那樣婴程。

代碼清單2-8 使用取反掩碼來進行符號擴展

function_name(long value)
{
  long mask = ~0x3;  // 0xfffffffc 或 0xfffffffffffffffc
  return (value & mask);
}

在這段代碼里廓奕,在64位情況下,掩碼的高位都是1档叔。

確保創(chuàng)建的數(shù)據(jù)結(jié)構(gòu)是固定長度和對齊的

當數(shù)據(jù)在你的app的32位和64位版本中共享的時候桌粉,你可能需要創(chuàng)建32位和64位表示都相同的數(shù)據(jù)結(jié)構(gòu),常見的場景是衙四,當數(shù)據(jù)保存在文件或者通過網(wǎng)絡傳輸交換到其他設備時铃肯,兩個設備的運行環(huán)境可能不同。同樣要記住的是传蹈,用戶可能吧他們的數(shù)據(jù)備份在32位的設備上押逼,然后在64位的設備上恢復。所以數(shù)據(jù)的互換性是你必須解決的問題惦界。

使用明確的整數(shù)類型

C99標準提供了明確寬度的內(nèi)置數(shù)據(jù)類型挑格,與底層硬件的體系結(jié)構(gòu)無關(guān)。當你的數(shù)據(jù)必須是固定寬度的或者你知道某個變量所有可能的值在有限的范圍內(nèi)表锻,你應該用上述的這些類型。通過使用一個合適的數(shù)據(jù)類型乞娄,你可以得到一個在內(nèi)存中固定寬度的類型瞬逊,并且你可以避免因為給變量分配了過大的內(nèi)存二導致的內(nèi)存浪費。
表格2-1列出了C99標準所有的整數(shù)類型和他們的取值范圍仪或。

表格2-1 C99的明確的整數(shù)類型

類型 范圍
int8_t -128 ~ 127
int16_t -32,768 ~ 32,767
int32_t -2,147,483,648 ~ 2,147,483,647
int64_t -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
uint8_t 0 ~ 255
uint16_t 0 ~ 65,535
uint32_t 0 ~ 4,294,967,295
uint64_t 0 ~ 18,446,744,073,709,551,615

謹慎地對64位整型進行對齊

在64位運行時中确镊,所有64位整數(shù)的對齊寬度都從4字節(jié)變?yōu)榱?字節(jié)。即使你明確指定了每個整數(shù)的類型范删,在32位和64位環(huán)境中兩個結(jié)構(gòu)體還是可能有所區(qū)別蕾域。在代碼清單2-9中,這個結(jié)構(gòu)體的對齊方式在不同位數(shù)的環(huán)境下會發(fā)生變化到旦,即使使用了明確的整型旨巷。
代碼清單2-9 結(jié)構(gòu)體的64位整數(shù)的對齊

struct bar {
  int32_t foo0;
  int32_t foo1;
  int32_t foo2;
  int64_t bar;
};

當這段代碼用32位的編譯器編譯時,成員bar從結(jié)構(gòu)體頭往后12字節(jié)處開始添忘。當同樣的代碼用64位編譯器編譯時采呐,成員bar從結(jié)構(gòu)體頭玩夠16字節(jié)處開始。在foo2成員后加了4字節(jié)的填充使得bar成員可以對齊到8字節(jié)邊界搁骑。
如果你在定義一個新的結(jié)構(gòu)體斧吐,把有最大對齊值的成員放在最前面又固,最小的放在最后面。這樣排布可以排除大部分的填充字節(jié)煤率。如果你在使用一個已存在的包含了不對齊整數(shù)的結(jié)構(gòu)體仰冠,你可以用pragma預編譯指令強制規(guī)定合適的對齊。代碼清單2-10中的結(jié)構(gòu)體和上面的一樣蝶糯,但是這里強制使用了32位的對齊規(guī)則洋只。

代碼清單2-10 使用pragma預編譯指令控制對齊

#pragma pack(4)
struct bar {
  int32_t foo0;
  int32_t foo1;
  int32_t foo2;
  int64_t bar;
};
#pragma options align=reset

只有當你需要的時候才使用這個選項,因為訪問不對齊的內(nèi)存有性能損耗裳涛。例如木张,你可以在使用你的32位版本的app內(nèi)存在的數(shù)據(jù)結(jié)構(gòu)時,使用這個選項來保持向下兼容端三。

用sizeof來分配內(nèi)存

永遠不要在調(diào)用malloc分配變量內(nèi)存時給明確的大邢侠瘛(例如malloc(4))。而應該用sizeof獲得任何結(jié)構(gòu)體或變量正確的大小后來分配內(nèi)存郊闯。在你的代碼里尋找任何沒有配合sizeof使用的malloc妻献。

使用正確的格式化字符串來支持兩種運行時

printf一樣的打印函數(shù)在支持兩種運行時的時候可能會有點棘手,因為數(shù)據(jù)類型發(fā)生了變化团赁。為了解決打印指針寬度的整數(shù)和其他標準的類型時的問題育拨,可以使用頭文件inttypes.h里定義的宏。
表格2-2展示了多種類型的格式化字符串欢摄。表格2-3展示了inttypes.h中定義的額外類型熬丧。

類型 格式化字符串
int %d
long %ld
long long %lld
size_t %zu
ptrdiff_t %td
任意指針 %p

表格2-2 標準格式化字符串

類型 格式化字符串
int %d
long %ld
long long %lld
size_t %zu
ptrdiff_t %td
任意指針 %p
類型 格式化字符串
int[N]_t(例如int32_t PRId[N](例如PRId32
uint[N]_t PRIu[N]
int_least[N]_t PRIdLEAST[N]
uint_least[N]_t PRIuLEAST[N]
int_fast[N]_t PRIdFAST[N]
uint_fast[N] PRIuFAST[N]
intptr_t PRIdPTR
uintptr_t PRIuPTR
intmax_t PRIdMAX
uintmax_t PRIuMAX

表格2-3 inttypes.h中額外的格式化字符串(N代表數(shù)字)

類型 格式化字符串
int[N]_t(例如int32_t PRId[N](例如PRId32
uint[N]_t PRIu[N]
int_least[N]_t PRIdLEAST[N]
uint_least[N]_t PRIuLEAST[N]
int_fast[N]_t PRIdFAST[N]
uint_fast[N] PRIuFAST[N]
intptr_t PRIdPTR
uintptr_t PRIuPTR
intmax_t PRIdMAX
uintmax_t PRIuMAX

例如,當需要打印一個intptr_t的變量(一個指針寬度的整數(shù))和一個指針時怀挠,你可以按照代碼清單2-11這樣來寫:
代碼清單2-11 體系結(jié)構(gòu)無關(guān)的打印輸出

#include <inttypes.h>
void *foo;
intptr_t k = (intptr_t)foo;
void *ptr = &k;

printf("The value of k is %" PRIdPTR "\n", k);
printf("The value of ptr is %p\n", ptr);

謹慎處理函數(shù)和函數(shù)指針

64位環(huán)境中的函數(shù)調(diào)用和32位中的處理方式不同析蝴。最嚴重的區(qū)別就是變參函數(shù)和定參函數(shù)使用不同的指令序列來讀取參數(shù)。代碼清單2-12展示了兩種函數(shù)的原型绿淋。第一個函數(shù)(fixedFunction)只接受一對整數(shù)作為參數(shù)闷畸。第二個函數(shù)接受可變數(shù)量的數(shù)作為參數(shù)(但至少兩個)。在32位環(huán)境中吞滞,這兩個函數(shù)使用類似的指令序列來讀取參數(shù)佑菩。在64位環(huán)境中,這兩個函數(shù)在編譯時就使用了不同的規(guī)則裁赠。

代碼清單2-12 函數(shù)類型不同殿漠,64位調(diào)用規(guī)則也不同

int fixedFunction(int a, int b);
int variadicFunction(int a, ...);

int main
{
  int value2 = fixedFunction(5, 5);
  int value1 = variadicFunction(5, 5);
}

因為64位環(huán)境下的調(diào)用規(guī)則更加精確了,所以你需要確保函數(shù)調(diào)用的正確性佩捞,以保證被調(diào)用者能找到調(diào)用者所傳遞的參數(shù)凸舵。

確保函數(shù)都定義了原型

當工程設置為最新時,編譯器會在代碼嘗試調(diào)用沒有明確原型的函數(shù)時生成錯誤失尖。給出明確原型是為了幫助編譯器確定函數(shù)是變參函數(shù)還是定參函數(shù)啊奄。

函數(shù)指針必須使用正確的原型

如果你在代碼中傳遞了一個函數(shù)的指針渐苏,那這個指針的調(diào)用規(guī)則必須和原函數(shù)保持一致。它必須接受和原來函數(shù)同樣的參數(shù)集合菇夸。不要把一個變參函數(shù)轉(zhuǎn)成定參函數(shù)(反之亦然)琼富。代碼清單2-13給出了一個有問題的函數(shù)調(diào)用的示例。因為這個函數(shù)指針被轉(zhuǎn)換后會使用不同的調(diào)用規(guī)則庄新,所以調(diào)用者會把參數(shù)放在被調(diào)函數(shù)所不期望的位置鞠眉。這種不匹配可能會導致app崩潰或者其他不可預測的行為。

代碼清單2-13 在變參函數(shù)和定參函數(shù)之間的轉(zhuǎn)換會導致錯誤

int MyFunction(int a, int b, ...);

int (*action)(int, int, int) = (int (*)(int, int, int))MyFunction;
action(1, 2, 3); // 錯誤择诈!

重要:如果你像這樣轉(zhuǎn)換一個函數(shù)械蹋,編譯器不會報任何錯誤和警告,并且在iOS模擬器中它的不確定的行為也是不可見的羞芍。所以一定要在發(fā)布app之前在真機上測試哗戈。

使用方法函數(shù)原型來分發(fā)Objective-C消息

上述轉(zhuǎn)換函數(shù)指針的規(guī)則有個例外,就是調(diào)用用來發(fā)送消息的objc_msgSend函數(shù)或者其他類似的Objective-C運行時函數(shù)荷科。盡管這些消息相關(guān)的函數(shù)的原型是可變參的唯咬,但是Objective-C運行時調(diào)用的這些方法函數(shù)的原型一般不盡相同。Objective-C運行時直接分發(fā)到實現(xiàn)了這個方法的函數(shù)畏浆,所以這里的調(diào)用規(guī)則就像上面描述的一樣胆胰,是不匹配的。所以你必須把objc_msgSend函數(shù)轉(zhuǎn)換成匹配那個被調(diào)用的方法函數(shù)的原型刻获。
代碼清單2-14展示了一個合理的例子蜀涨,它用底層的消息函數(shù)把一條消息發(fā)給了一個對象。在這個例子中蝎毡,doSomething:方法接受一個參數(shù)厚柳,并且沒有不可變的版本。它使用這個方法函數(shù)的原型來轉(zhuǎn)換objc_msgSend顶掉。注意到一個方法函數(shù)第一個參數(shù)永遠是id類型的變量草娜,第二個參數(shù)是一個選擇器(selector)挑胸。在objc_msgSend轉(zhuǎn)換成一個函數(shù)指針后痒筒,這個調(diào)用被分發(fā)到這個函數(shù)指針。

代碼清單2-14 使用強轉(zhuǎn)來調(diào)用Objective-C消息發(fā)送的函數(shù)

- (int)doSomething:(int)x { ... }
- (void)doSomeThingElse {
  int (*action)(id, SEL, int) = (int (*)(id, SEL, int))objc_msgSend;
  action(self, @selector(doSomething:), 0);
}

謹慎地調(diào)用變參函數(shù)

可變參數(shù)列表(varargs)不提供參數(shù)的類型信息茬贵,而且這些參數(shù)不會自動轉(zhuǎn)換到更寬的類型簿透。如果你希望分辨不同的入?yún)㈩愋停阈枰酶袷交址蛘咂渌愃频臋C制來提供varargs的類型信息解藻。如果調(diào)用方?jīng)]有正確地給出這些信息(或者varargs函數(shù)沒有解釋正確)老充,結(jié)果會出錯。
特別地螟左,如果你的varargs函數(shù)需要一個long型整數(shù)啡浊,但你傳了一個32位值觅够,那么這個varargs函數(shù)就獲得了32位數(shù)據(jù)和后一個參數(shù)的32位無效信息(這32位是必然會丟失的)。類似地巷嚣,如果你的varargs函數(shù)需要一個int類型而你傳了一個long型數(shù)喘先,你只能得到一半的數(shù)據(jù),并且剩下的另一半會錯誤地出現(xiàn)在下一個參數(shù)中廷粒。(這種情況的例子是在printf中使用了錯誤的格式化字符串窘拯。)

不要直接獲取Objective-C指針

如果你在代碼中直接獲取一個對象的isa成員,在64位環(huán)境中這段代碼不會通過坝茎。isa不再保存一個指針了涤姊。它現(xiàn)在包含了一些指針數(shù)據(jù),余下的位來存放其他運行時數(shù)據(jù)嗤放。這個優(yōu)化改進了內(nèi)存使用并增強了性能思喊。
現(xiàn)在取得對象的isa內(nèi)的信息可以用class屬性或者調(diào)用object_getClass函數(shù)。往isa里寫東西斤吐,則是調(diào)用object_setClass函數(shù)搔涝。

重要:因為這些錯誤不會被iOS模擬器發(fā)現(xiàn),所以請在真機測試app和措。

使用內(nèi)置的同步原語

有時app會實現(xiàn)自己的同步原語來提高性能庄呈。iOS運行時提供了一整套同步原語,而它在運行的時候是特別為了各種CPU而優(yōu)化了的派阱。這個運行時庫位新體系結(jié)構(gòu)提供了一些新特性诬留。已經(jīng)用了這個庫的32位app會在64位環(huán)境中自動獲得新的CPU特性。如果你在app的舊版中實現(xiàn)了自定義的原語贫母,現(xiàn)在會比內(nèi)置的原語慢若干個數(shù)量級文兑。所以請使用內(nèi)置原語。

永遠不要硬編碼虛存的頁面大小

大部分app沒必要知道虛存的頁面大小腺劣,然而有些app困難會需要這個值來分配緩存空間或是調(diào)用一些框架窍霞。從iOS7開始炫彩,32位和64位設備的虛存頁面大小會不相同。所以當需要獲得虛存頁面大小時,使用getpagesize()函數(shù)盖淡。

實踐地址無關(guān)性

64位運行時只支持地址無關(guān)可執(zhí)行文件(PIE)±雇铮現(xiàn)代app編譯時默認都是地址無關(guān)的媚送。如果你有一些阻止編譯成地址無關(guān)代碼的東西噩咪,例如靜態(tài)鏈接庫或者匯編代碼,你需要在轉(zhuǎn)移到64位環(huán)境時更新你的代碼芋酌。32位環(huán)境下地址無關(guān)性也是極度推薦的增显。
更多信息請見編譯生成一個地址無關(guān)的可執(zhí)行文件

使你的app在32位環(huán)境下運行良好

目前為了64位環(huán)境寫的app也要支持32位環(huán)境脐帝。所以你需要使得你的app在兩種環(huán)境下都運行良好同云。通常這意味著一種設計要支持兩種環(huán)境糖权。偶爾也會需要為不同環(huán)境給出不同的解決方案。
例如炸站,你有可能不得不在整個代碼中用64位整數(shù)温兼;兩種環(huán)境都支持64位整數(shù),并且在所有地方使用同一種整數(shù)類型會使得app的設計變得簡單武契。如果你在任何地方都用64位整數(shù)募判,app在32位環(huán)境下會運行得較為緩慢。64位處理器處理64位整數(shù)和32位整數(shù)的速度一樣快咒唆,但是32位處理器在進行64位計算時會慢很多届垫。而且在兩種環(huán)境中變量會比實際需要的花費更多內(nèi)存。一個變量應當用符合值的范圍的整數(shù)類型全释。如果計算值域在32位整數(shù)內(nèi)就用32位整數(shù)装处。參見使用明確的整數(shù)類型;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市浸船,隨后出現(xiàn)的幾起案子妄迁,更是在濱河造成了極大的恐慌,老刑警劉巖李命,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件登淘,死亡現(xiàn)場離奇詭異,居然都是意外死亡封字,警方通過查閱死者的電腦和手機黔州,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阔籽,“玉大人流妻,你說我怎么就攤上這事“手疲” “怎么了绅这?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長在辆。 經(jīng)常有香客問我证薇,道長,這世上最難降的妖魔是什么开缎? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任棕叫,我火速辦了婚禮林螃,結(jié)果婚禮上奕删,老公的妹妹穿的比我還像新娘。我一直安慰自己疗认,他們只是感情好完残,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布伏钠。 她就那樣靜靜地躺著,像睡著了一般谨设。 火紅的嫁衣襯著肌膚如雪熟掂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天扎拣,我揣著相機與錄音赴肚,去河邊找鬼。 笑死二蓝,一個胖子當著我的面吹牛誉券,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播刊愚,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼踊跟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鸥诽?” 一聲冷哼從身側(cè)響起商玫,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎牡借,沒想到半個月后拳昌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡钠龙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年地回,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俊鱼。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡刻像,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出并闲,到底是詐尸還是另有隱情细睡,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布帝火,位于F島的核電站溜徙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏犀填。R本人自食惡果不足惜蠢壹,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望九巡。 院中可真熱鬧图贸,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至沟优,卻和暖如春涕滋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背挠阁。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工宾肺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人侵俗。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓爱榕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親坡慌。 傳聞我的和親對象是個殘疾皇子黔酥,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容