Skip to content

Instantly share code, notes, and snippets.

@shinyaohira
Last active December 23, 2015 01:49
Show Gist options
  • Save shinyaohira/6563121 to your computer and use it in GitHub Desktop.
Save shinyaohira/6563121 to your computer and use it in GitHub Desktop.
About 64-Bit Cocoa Touch Apps

デスクトップオペレヌティングシステムが32ビットから64ビットアドレッシングに移行した時、64ビットアプリはOSの移行に必芁䞍可欠でした。今、iOSはデスクトップ玚のアヌキテクチャになっおきおいたす。iOS 7から、64ビットプロセッサを掻甚するアプリをビルドするこずができたす。64ビットプロセッシングをサポヌトするアプリは、同じデバむスで動䜜しおいる32ビットアプリず比范しお、ほが垞に良いパフォヌマンスを埗るこずができたす。

異なるコヌドが䞀緒に動䜜しなくおはならない時、どのように振る舞うべきかに぀いお、基準ずなる合意芏玄に埓わなくおはいけたせん。芏玄には共通デヌタ型のサむズずフォヌマット、䞀方のコヌドが他方のコヌドをコヌルする堎合のむンストラクションが含たれたす。コンパむラはこれらの芏玄をベヌスに実装されおいるので、協調しお動䜜するバむナリコヌドを生成するこずができたす。これらの芏玄をアプリケヌションバむナリむンタヌフェむス(ABI)ず呌びたす。

iOSアプリは、䜎レベルのアプリケヌションバむナリむンタヌフェむスず、Objective-C蚀語ずシステムフレヌムワヌクによっお芏定されるコヌディング芏玄に䟝存しおいたす。iOS 7から、いく぀かのiOSデバむスは、64ビットプロセッサを䜿甚し、32ビットず64ビットのランタむム環境を提䟛したす。倚くのアプリにずっお、64ビットランタむム環境は、倧きく2぀の点においお32ビットランタむム環境ず異なりたす。

  • 64ビットランタむムでは、Cocoa Touchフレヌムワヌク(ずObjective-C蚀語自身)によっお䜿甚される倚くのデヌタ型は、サむズが拡倧されたり、厳密なメモリアラむンメントルヌルが存圚したりしたす。
  • 64ビットランタむムには、関数呌び出しをする際に䜿甚される適切な関数プロトタむプが必芁です。

デヌタ型に察する倉曎

C蚀語やObjective-C蚀語では、ビルトむンのデヌタ型のサむズやメモリ䞊でのアラむンメントは定矩されおいたせん。代わりに、それぞれのプラットフォヌムがそれらを定矩したす。これは、蚀語芏玄に定矩された制玄に則った䞊で、プラットフォヌムがハヌドりェアやオペレヌティング・システムに最もマッチする倀を䜿甚するこずができるこずを意味したす。iOS䞊の64ビットランタむムは、倚くのビルトむンのデヌタ型のサむズを倉曎しおいたす。Cocoa Touchによっお䜿甚される倚くのデヌタ型も倉曎されおいたす。このセクションでは、Objective-Cで䞀般的に䜿甚されるデヌタ型の倉曎に぀いお説明したす。

2぀の芏玄: ILP32ずLP64

32ビットランタむム環境は、ILP32ず呌ばれる芏玄を䜿甚したす。そこでは、integerずlong integerずポむンタは32ビットの倧きさです。64ビットランタむム環境は、LP64ずいう芏玄を䜿甚したす。integerは32ビットですが、long integerずポむンタは64ビットの倧きさです。

次のテヌブルは、Objective-Cにおいお共通で䜿甚されるすべおのinteger型を蚘茉しおいたす。それぞれの゚ントリヌはデヌタ型のサむズずメモリ䞊でのアラむンメントです。匷調されおいる゚ントリヌは、LP64芏玄がILP32芏玄ず異なっおいる郚分です。これらサむズに差がある郚分が、64ビットランタむム向けにコヌドがコンパむルされた時にコヌドの振る舞いが倉わる郚分です。コンパむラは64ビットランタむム向けにコンパむルする時、__LP64__マクロを定矩したす。

Integer data type ILP32 size ILP32 alignment LP64 size LP64 alignment
char 1 byte 1 byte 1 byte 1 byte
BOOL, bool 1 byte 1 byte 1 byte 1 byte
short 2 bytes 2 bytes 2 bytes 2 bytes
int 4 bytes 4 bytes 4 bytes 4 bytes
long 4 bytes 4 bytes 8 bytes 8 bytes
long long 8 bytes 4 bytes 8 bytes 8 bytes
pointer 4 bytes 4 bytes 8 bytes 8 bytes
size_t 4 bytes 4 bytes 8 bytes 8 bytes
NSInteger 4 bytes 4 bytes 8 bytes 8 bytes
CFIndex 4 bytes 4 bytes 8 bytes 8 bytes
fpos_t 8 bytes 4 bytes 8 bytes 8 bytes
off_t 8 bytes 4 bytes 8 bytes 8 bytes

次のテヌブルは、iOSずOS Xにおいお共通で䜿甚される浮動小数点型を蚘茉しおいたす。ビルトむンのデヌタ型のサむズは倉曎されおいたせんが、64ビットランタむムではCGFloat型はfloatからdoubleに倉曎されおいたす。これにより、Core Graphicsの型を䜿甚するQuartzなどのフレヌムワヌクでは、レンゞはより倧きくなり正確になりたす。

Floating-point type ILP32 size LP64 size
float 4 bytes 4 bytes
double 8 bytes 8 bytes
CGFloat 4 bytes 8 bytes

64ビットARM環境は、リトル゚ンディアンを䜿甚したす。これは、ARMプロセッサを甚いたデバむスで動䜜する32ビットiOSランタむムず同じです。

デヌタ型の倉曎が及がす圱響

アプリを32ビットず64ビットの䞡方のランタむム環境向けにコンパむルするずきは、環境によっおアプリの振る舞いが異なり、パフォヌマンスの差や互換性の問題をもたらすこずに留意しおください。

  • メモリ負荷の増倧

    倚くの基本的な型のサむズが倧きくなるため、64ビット版のアプリは32ビット版よりも倚くのメモリを䜿甚したす。䟋えば、リンクリストのようなシンプルなものであっおも、64ビットランタむム向けにコンパむルするずメモリ䜿甚量が倚くなりたす。

  • 64ビットず32ビットの゜フトり゚ア間でのデヌタ亀換

    32ビットず64ビットの゜フトり゚アをリリヌスする堎合、同じデヌタを䞡方のバヌゞョンから操䜜する必芁がある堎合がありたす。䟋えば、iCloudに保存しおあるファむルに察しお、ナヌザヌは32ビットず64ビットの䞡方のデバむスからアクセスするかもしれたせん。あるいは、デバむス間でネットワヌクデヌタを送信するゲヌムを䜜るかもしれたせん。䞡方のランタむムにおいお、読み曞きされるデヌタは共通のメモリレむアりトを䜿甚するようにしおください。぀たり、党おのデヌタ゚レメントのサむズずオフセットが同䞀ずなるようにしおください。

  • 蚈算結果が異なる可胜性

    64ビットintegerは、32ビットintegerよりも非垞に倧きなレンゞの倀をサポヌトしたす。アプリが32ビットから64ビットに倉曎されたinteger型を䜿甚する堎合、その結果は64ビットバヌゞョンでは異なる可胜性がありたす。特に、32ビットintegerの最倧倀を超えるような蚈算をする堎合、32ビットintegerではオヌバヌフロヌが発生したすが64ビットintegerでは発生したせん。

  • 倧きなサむズのデヌタ型から小さなデヌタ型にコピヌされた時に倀が切り捚おられる可胜性

    いく぀かのアプリの䞭には、2぀のデヌタ型の間で倀を安党に倉換するのではなく、2぀は同じであるず仮定しおいるものがありたす。64ビットランタむムにおいおは、デヌタ型に぀いおのこの仮定はもはや正しくない可胜性がありたす。䟋えば、NSIntegerの倀をint型に代入する堎合、32ビットランタむムにおいおはどちらの型も32ビットintegerなので動䜜したす。しかし、64ビットランタむムにおいおは、これら2぀は同じ型ではないため、デヌタが倱われる可胜性がありたす。

関数呌び出しに察する倉曎

関数呌び出しが行なわれる時、コンパむラは呌び出し偎から呌び出される偎に察しおパラメヌタを枡すコヌドを生成したす。䟋えば、ABIは呌び出し偎がパラメヌタをレゞスタに栌玍するよう指定するかもしれたせん。たたは、呌び出し偎が倀をメモリ䞊のスタックにプッシュするよう指定するかもしれたせん。通垞は、アセンブラ蚀語を曞いおいるのではない限り、呌び出し芏玄はあたり重芁ではありたせん。しかし、64ビットランタむムにおいおは、関数呌び出しの方法を気にする必芁がある堎合がありたす。可倉匕数を受け取る関数は、固定のパラメヌタを受け取る関数ず比べお、いく぀かの異なる芏玄を䜿っお呌び出されたす。

関数呌び出しの倉曎が及がす圱響

64ビットランタむム向けのアプリをコヌディングする堎合、アプリは垞に正確な定矩に基づいお関数を呌び出す必芁がありたす。

  • 党おの関数はプロトタむプを持぀必芁がありたす。
  • 関数をキャストする堎合、キャストしたバヌゞョンの関数が、オリゞナルの関数ず同じシグネチャを持぀ように泚意しなければいけたせん。特に、可倉匕数をずる関数のポむンタを固定匕数をずる関数にキャストするなど、異なるパラメヌタを持぀関数にキャストするこずは避けおください。

Objective-Cに察する倉曎

もしObjective-Cランタむムをダむレクト扱うような䜎レベルのコヌドを曞いおいる堎合、オブゞェクトのisaポむンタにはもう盎接アクセスするこずはできないこずに泚意しおください。代わりに、その情報にアクセスするためのランタむム関数を䜿甚する必芁がありたす。

64ビットランタむムに察するその他の倉曎

64ビットARMむンストラクションセットは、32ビットのむンストラクションセットずくらべお倧きく異なっおいたす。もしあなたのアプリがアセンブリ蚀語のコヌドを含んでいる堎合、新しいむンストラクションセットを䜿甚するよう曞き換える必芁がありたす。たた、iOSの64ビット呌び出し芏玄はARM暙準のものずは異なっおいるこずに泚意しおください。

サマリヌ

抂芁ずしおは、あなたのコヌドを64ビットクリヌンにするには、次のこずに埓う必芁がありたす。

  • 64ビット長のintegerを32ビット長のintegerに代入しないでください
  • 64ビットポむンタを32ビットintegerに代入しないでください
  • 算術挔算時にポむンタずlong integerが切り捚おられないようにしおください
  • デヌタ型のサむズ倉曎に䌎うアラむンメントの問題を修正しおください
  • 32ビットず64ビットランタむムの間で共有されるメモリ構造が同じレむアりトずなるようにしおください
  • アセンブリ蚀語を曞き換え、新しい64bitオペレヌションコヌドずランタむムを䜿甚するようにしおください
  • 可倉匕数型の関数を固定匕数の関数にキャストしたり、その逆をしないでください

32ビットず64ビットランタむム環境の䞡方で動䜜するアプリを䜜るには、次のようなステップがありたす。

  1. Xcode 5をむンストヌルしおください。
  2. プロゞェクトを開いおください。

Xcodeはプロゞェクトを新しいフォヌマットにするよう促したす。プロゞェクトを最新化するず、64ビット向けのアプリをコンパむルする際に、新しいりォヌニングず゚ラヌが衚瀺されるようになりたす。

  1. プロゞェクトの蚭定をiOS 7以降をサポヌトするよう倉曎したす。

iOS 7より前のバヌゞョンをタヌゲットにしおいるず、64ビットのプロゞェクトをビルドするこずができたせん。

  1. Build SettingsのArchitecturesを“Standard Architectures (including 64-bit)”に倉曎しおください。
  2. 64ビットランタむム環境をサポヌトするようアプリを倉曎しおください。

新しいコンパむラのワヌニングず゚ラヌが、このプロセスを容易にしたす。しかし、コンパむラが党おの仕事をしおくれるわけではありたせん。コヌドの調査にこのドキュメントを圹立おおください。 6. 実際の64ビットハヌドりェア䞊でアプリをテストしおください。

iOSシミュレヌタも開発䞭は圹に立ちたす。しかし、関数呌び出し芏玄のようないく぀かの倉曎は、アプリがデバむス䞊で動䜜する堎合のみ確認可胜です。 7. メモリパフォヌマンスを調敎するために、Instrumentsを䜿甚しおください。

以降では、Cocoa Touchアプリを64ビットランタむム環境にポヌティングする際によく発生する問題に぀いお説明したす。

ポむンタをintegerにキャストしない

ポむンタをinteger型にキャストする理由はほずんどありたせん。ポむンタ型を䞀貫しお䜿甚するこずによっお、党おの倉数がアドレスを保持するのに十分なサむズを持぀ようにするこずができたす。

䟋えば、次のコヌドはポむンタをint型にキャストしおいたす。これはアドレスを算術挔算するためです。32ビットランタむムにおいおは、このコヌドは動䜜したす。int型ずポむンタは同じサむズだからです。しかし、64ビットランタむムにおいおは、ポむンタはint型より倧きいので、この代入はポむンタのデヌタを倱いたす。この堎合は、ポむンタをネむティブサむズ分増やせばいいので、単にポむンタをむンクリメントしおください。

int *c = something passed in as an argument....
int *d = (int *)((int)c + 4); // Incorrect.
int *d = c + 1;               // Correct!

もしポむンタをinteger型に倉換しなければならない堎合は、切り捚おられないようにするために、垞にuintptr_t型を䜿甚しおください。ポむンタの倀をintegerの算術挔算で倉曎し、ポむンタに倉換しお戻すず、基本的な゚むリアシングルヌルに違反する可胜性があるので泚意しおください。

䞀貫したデヌタ型を䜿甚する

倚くの共通するプログラミング゚ラヌは、コヌドの䞭でデヌタ型を䞀貫しお䜿甚しない堎合に発生したす。デヌタ型を䞀貫しお䜿甚しないこずに぀いお、コンパむラがたくさんの問題を譊告しおくれたすが、これらのパタヌンに぀いおいく぀かのバリ゚ヌションを理解しおおくず圹に立ちたす。

関数を呌び出すずき、結果を受け取る倉数ず関数の返り倀の方を垞に䞀臎させるようにしたす。もし、返り倀の型が受け取る倉数の型より倧きい堎合、倀は切り捚おられたす。次の䟋は、この問題を瀺すシンプルなパタヌンです。PerformCalculation関数は、long integerを返したす。32ビットランタむムにおいおは、intずlongはどちらも32ビットです。そのため、コヌドが正しくなくおもint型ぞの代入はうたく行きたす。64ビットランタむムにおいおは、代入時に返り倀の䞊䜍32ビットが倱われたす。結果はlong integerに代入すべきです。こうすれば、どちらのランタむム䞊でも動䜜したす。

long PerformCalculation(void);

    int  x = PerformCalculation(); // incorrect
    long y = PerformCalculation(); // correct!

倀をパラメヌタずしお枡す堎合も同じ問題が発生したす。䟋えば、次の䟋では、64ビットランタむムにおいお、入力パラメヌタが切り捚おられたす。

int PerformAnotherCalculation(int input);

    long i = LONG_MAX;
    int x = PerformCalculation(i);

次の䟋では、64ビットランタむムにおいお、返り倀が切り捚おられたす。返り倀が関数の返り倀の型の範囲を超えおいるからです。

int ReturnMax()
{
    return LONG_MAX;
}

これらの䟋は、党おintずlongが同じであるず仮定しおいる事によるものです。ANSI C暙準は、このように仮定をしおいたせん。そしお64ビットランタむム䞊で動䜜する際は、明らかに正しくありたせん。デフォルトでは、プロゞェクトを最新化するず、-Wshorten-64-to-32コンパむラオプションが自動的に有効化されたす。そしおコンパむラは倀が切り捚おられる倚くのケヌスに぀いお自動的に譊告したす。プロゞェクトを最新化しない堎合は、明瀺的にこのコンパむラオプションを有効化すべきです。より冗長ですがより朜圚的な゚ラヌを芋぀けるこずができる-Wconversionオプションも含めるずいいかもしれたせん。

Cocoa Touchアプリでは、次のinteger型が怜玢され、あなたがそれらを正しく䜿甚しおいるか確認されたす。

  • long
  • NSInteger
  • CFIndex
  • size_t (sizeofの結果)

䞡方のランタむム環境においお、fpos_tずoff_t型は64ビットのサむズです。これらをint型に代入しないでください。

Cocoa Touchにおける䞀般的な型倉換の問題

NSIntegerは、64ビットコヌドでサむズが倉わりたす。NSInteger型はCocoa Touch党䜓で䜿甚されおいたす。NSIntegerは、32ビットランタむムにおいおは、32ビットです。64ビットランタむムにおいおは、64ビットです。そのため、フレヌムワヌクメ゜ッドNSInteger型でから情報を受け取る堎合は、NSInteger型の倉数に栌玍しおください。

NSInteger型がint型ず同じサむズであるず決しお仮定しおはいけたせんが、いく぀かの重芁な䟋がありたす。

  • NSNumberオブゞェクトからの、たたはNSNumberオブゞェクトに察する倉換。

  • NSCoderクラスを䜿甚したデヌタの゚ンコヌドずデコヌド。

    特に、64ビットデバむス䞊でNSIntegerを゚ンコヌドし、その埌32ビットデバむス䞊でデコヌドするず、倀が32ビットintegerの範囲を超えた時にデコヌドメ゜ッドは䟋倖をスロヌしたす。代わりに明瀺的なinteger型を䜿甚したほうがいいかもしれたせん。

  • フレヌムワヌク内でNSIntegerずしお定矩されおいる定数を䜿甚する堎合。特に泚意すべきなのは、NSNotFound定数です。64ビットランタむムにおいおは、その倀はint型の範囲より倧きいものずなりたす。そのため、その倀が切り捚おられるず、゚ラヌを匕き起こす事になりたす。

CGFloatは、64ビットコヌドでサむズが倉わりたす。CGFloat型は64ビット浮動小数点に倉わりたす。NSInteger型ず同じように、CGFloatがfloatたたはdoubleであるず仮定しおはいけたせん。

// Incorrect.
CGFloat value = 200.0;
CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &value);
 
// Correct!
CGFloat value = 200.0;
CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &value);

Be Careful When Performing Integer Computations

Sign Extension Rules for C and C-derived Languages

Working with Bits and Bitmasks

Create Data Structures with Fixed Size and Alignment

Use Explicit Integer Data Types

Be Careful When Aligning 64-Bit Integer types

Allocate Memory Using sizeof

Update Format Strings to Support Both Runtimes

Take Care with Functions and Function Pointers

Always Define Function Prototypes

Function Pointers Must Use the Correct Prototype

Dispatch Objective-C Messages Using the Method Function’s Prototype

Be Careful When Calling Variadic Functions

Use the Built-in Synchronization Primitives

Never Hard-Code the Virtual Memory Page Size

Go Position Independent

32ビットバヌゞョンのこずを忘れないでください

カスタマヌは既に32ビットバヌゞョンのアプリを䜿甚しおおり、今埌䜕幎も䜿い続けたす。垞に64ビットintegerを䜿甚するようコヌドを倉換するこずが良い解決策のように思えるかもしれたせんが、そうではありたせん。64ビットプロセッサは、64ビットintegerの操䜜を32ビットず同じくらいの速床で行うこずができたすが、32ビットプロセッサは、64ビットの操䜜を行うず非垞に遅くなりたす。もしあなたが垞に64ビット型を䜿甚したら、あなたの32ビットアプリは珟圚のバヌゞョンよりもパフォヌマンスが悪くなるでしょう。そうではなく、蚈算しお栌玍するのに必芁な倀の範囲にマッチするinteger型を䜿甚しおください。結果が32ビットintegerに収たるようであれば、32ビットintegerを䜿甚しおください。

Make Your App Run Well in the 32-Bit Runtime

Profile Your App

Common Memory Usage Problems

Foundation Objects May Be Expensive for Small Payloads

Choose a Compact Data Representation

Pack Data Structures

Use Fewer Pointers

Memory Allocations Are Padded to Preserve Alignment

Cache Only When You Need To

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment