あなたの最初の考えは、「オーケー、メモリ不足の警告でビューを手動でアンロードするにはどうすればよいですか? 一歩後退したように見えます。」
次に、答えを探して次のように書きます。
- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; if([self isViewLoaded] && ![[self view] window]) { [self setView:nil]; } }
ただし、このメソッドを使用する必要はありません。UIViewの基本サポートの変更により、潜在的に有害です。 したがって、実際には、メモリ不足を報告するときに、メモリからコントローラのビューを手動でアンロードする必要はほとんどありません。 (そして理論的には決してない)
しかし、なぜですか? iOSでのプログラミングに関する本を注意深く読んだ場合、UIViewはUIResponderのサブクラスであり(ビューがイベントと対話できるように)、CALayerのインスタンスへのポインターを持っていることがわかります(ビューを画面に描画できるように)。
CALayerは、 ビットマップイメージのコンテナーです。 UIViewがdrawInRectメソッドで描画されるとき:レイヤーのビットマップを作成します。 他のレイヤー変数(多くは、フレームやbackgroundColorなど、ビューから取得)は、このイメージが画面上のどのように、どこにあるかを示します。
ただし、レイヤーの主要部分(メモリ使用量)はビットマップです。 レイヤー自体は48バイトで、標準UIViewは画面サイズに関係なく96バイトです。 レイヤーのメモリ消費は、画面上の画像ビットマップのサイズに依存します。 たとえば、iPad Retinaの場合、フルスクリーン画像は12MBに達する可能性があります。
iOS6で使用されるアプローチは、十分なメモリがない場合、レイヤーのビットマップがメモリからアンロードされ、UIViewおよびCALayerオブジェクトがそのまま保持されることです。 これは非常にお勧めです。 前述のように、オブジェクト自体はかなり少量のメモリを占有し、drawInRectを使用してビットマップを再度作成できます。
したがって、このアプローチでは何が得られますか。メモリ不足についての警告の後、コントローラーはビューにデータを入力する必要がなくなります。 たとえば、いくつかのテキストフィールドを持つviewControllerを作成しました。 メモリが不足している場合、コントローラーはこれらのフィールドにテキストを保存し、viewDidLoadまたはviewWillAppear:を呼び出すときにそれらを再入力する必要があります。 テキストフィールドは決して破壊されず、画像が再び描画されるまでテキストを保持するため、これはもはや必要ありません。 これにより、多くのエラーが発生するコード部分が簡素化されます。
レイヤーレンダリングプロセスに組み込まれているいくつかの本当にクールな機能もあります。 しかし、最初に、メモリの割り当てと割り当て解除をよく理解しているかどうかを確認しましょう。 メモリが割り当てられると(オブジェクト、ビットマップ、それは問題ではありません)、ヒープ上の対応するブロックサイズが「使用済み」としてマークされ、ポインターがこのブロックの先頭に返されます。 オブジェクトにメモリを割り当てる場合、このポインタをオブジェクトと考えますが、実際にはメモリアドレスへのポインタにすぎません。
メモリブロックが「使用」されると、返されたポインタを介さずにこのメモリブロックの使用を防止する保護メカニズムがあります。 したがって、このメモリを使用するのはかなり困難です。
割り当て解除は、この保護を単に解除し、メモリブロックを「使用しない」とマークします。 これは、次のメモリ割り当てがこのブロックのすべてまたは一部を使用できることを意味します。 メモリを解放するとき、このブロックに保存されている値は変更されません。 同時に、別の場所から変更される可能性があるという事実にもかかわらず、まだアクセスする機会があります。 したがって、割り当てによって取得されたポインタを介してメモリにアクセスすることは決して安全ではありません。
次に、ビューとそのレイヤーでこれが重要な理由について説明します。 各レイヤーには、ビットマップを保存するオブジェクトを指すコンテンツプロパティがあります。
これは、プライベートオブジェクト、現在のビットマップ、および一部のメタデータ(画像にアルファチャネルがあるかどうか、ピクセルあたりのバイト数など)を含むCABackingStoreクラスのインスタンスです。 したがって、メモリがなくなったときに破棄する必要があるのはCABackingStoreです。
ただし、微妙な点は、メモリが不足してもCABackingStore(ストレージ)が破壊されないことです。 メモリ不足に関する警告の時点でビューがアクティブでない場合、そのレイヤーのストレージにはvolatileのフラグが立てられます。 このコンテキストでは、このフラグは割り当て解除と同じであり、さまざまな目的での使用へのアクセスを提供します。 フラグと割り当て解除の違いは、「タグ付き」メモリを実際に復元できることであり、回復不能なほど失われることはありません。
これが優れた最適化である理由を検討してください。 ビュー(CABackingStore)がビュー(またはレイヤー)で破棄された場合、次回はdrawInRectを実行してビューを再作成する必要があります。 これはリソースを大量に消費する操作なので、避ける必要があります。 ストレージ層を復元する機能があると、drawInRect:の新しい呼び出しを回避できます。
もちろん、ストアにフラグが立てられた瞬間と、ビューを表示する必要がある瞬間の間に、メモリが他の目的のために完全に使い果たされる可能性があります。 この場合、ビットマップ呼び出しを作成するには、drawInRect:を避けることはできません。