フィリップミルズの答えは正しいです。これは単なるその拡張です。
システムは文書化されているとおりに動作しています。
ナビゲーションコントローラにプッシュされているビューコントローラがの新しいインスタンスであるため、viewDidLoadが表示されています。それはコールビューDidLoadする必要があります。
もう少し調べてみると、それぞれのビューコントローラはポップされたときに割り当て解除されます(ブレークポイントまたはNSLogをdeallocに置くだけです)。この割り当て解除は、View Controllerコンテナとは関係がありません。使用するコントローラの寿命を制御するものではありません。
コントローラがナビゲーションコントローラスタックからポップされると、navコントローラはそのリファレンスを解放し、他の参照がないので、ビューコントローラはデアロックします。
ナビゲーションコントローラは、アクティブスタック内にあるビューコントローラへの強い参照のみを保持します。
同じコントローラを再利用する場合は、を再利用してください。ストーリーボードのセグを使用すると、そのコントロールを放棄します(大部分)。
push
ボタンをタップした結果コントローラFoo
を表示するとします。そのボタンがタップされると、 "システム"はFoo
(デスティネーションビューコントローラ)のインスタンスを作成し、次にsegueを実行します。コントローラコンテナは、そのビューコントローラへの唯一の強力な参照を保持するようになりました。それが完了すると、VCはデアロックします。
毎回新しいコントローラを作成するので、そのコントローラが提示されるたびにviewDidLoad
が呼び出されます。
この動作を変更し、後で再利用できるようにビューコントローラをキャッシュする場合は、具体的に行う必要があります。ストーリーボードセグを使用しない場合は、実際にVCをナビコントローラにプッシュ/ポップするので簡単です。
ただし、ストーリーボードのセグを使用する場合は、もう少し問題があります。
これを実行するにはいくつかの方法がありますが、いくつかの形式のハッキングが必要です。ストーリーボード自体が新しいView Controllerのインスタンス化を担当しています。 1つの方法は、instantiateViewControllerWithIdentifier
を無効にすることです。これは、SegueがView Controllerを作成する必要があるときに呼び出されるメソッドです。これは、識別子を与えないコントローラの場合でも呼び出されます(割り当てられていない場合、システムは作成された一意の識別子を提供します)。
これは主に教育目的のためです。私は確かに、あなたの問題を解決するための最良の方法として、これが何であろうとも、これを示唆していません。
のような何か...
@interface MyStoryboard : UIStoryboard
@property BOOL shouldUseCache;
- (void)evict:(NSString*)identifier;
- (void)purge;
@end
@implementation MyStoryboard
- (NSMutableDictionary*)cache {
static char const kCacheKey[1];
NSMutableDictionary *cache = objc_getAssociatedObject(self, kCacheKey);
if (nil == cache) {
cache = [NSMutableDictionary dictionary];
objc_setAssociatedObject(self, kCacheKey, cache, OBJC_ASSOCIATION_RETAIN);
}
return cache;
}
- (void)evict:(NSString *)identifier {
[[self cache] removeObjectForKey:identifier];
}
- (void)purge {
[[self cache] removeAllObjects];
}
- (id)instantiateViewControllerWithIdentifier:(NSString *)identifier {
if (!self.shouldUseCache) {
return [super instantiateViewControllerWithIdentifier:identifier];
}
NSMutableDictionary *cache = [self cache];
id result = [cache objectForKey:identifier];
if (result) return result;
result = [super instantiateViewControllerWithIdentifier:identifier];
[cache setObject:result forKey:identifier];
return result;
}
@end
は今、あなたはこのストーリーボードを使用する必要があります。残念ながら、UIApplicationはメインのストーリーボード上に保持されていますが、APIを公開することはありません。ただし、各ビューコントローラには、作成されたストーリーボードを取得するメソッドstoryboard
があります。
自分のストーリーボードを読み込んでいる場合は、MyStoryboardをインスタンス化するだけです。デフォルトのストーリーボードを使用している場合は、特別なものを使用するようにシステムを強制する必要があります。繰り返しますが、これを行う方法はたくさんあります。 1つの簡単な方法は、ビューコントローラ内のストーリーボードアクセサメソッドをオーバーライドすることです。
MyStoryboardは、すべてをUIStoryboardに転送するプロキシクラスにすることも、メインストーリーボードをisa-swizzleすることもできます。あるいは、ローカルコントローラがストーリーボードメソッドから返すこともできます。
ここで問題があることを覚えておいてください。スタック上で同じビューコントローラを複数回プッシュするとどうなりますか?キャッシュの場合、全く同じビューコントローラオブジェクトが複数回使用されます。それは本当にあなたが望むものですか?
もしそうでなければ、コントローラーコンテナ自体とのやりとりを管理して、コントローラーが既にそのコントローラーを知っているかどうかを確認する必要があります。その場合、新しいインスタンスが必要です。
だから、あなたはデフォルトで何を得るそこは(実際には非常にいくつかの方法があります)、デフォルトのストーリーボードのseguesを使用している間、キャッシュされたコントローラを取得する方法です...しかし、それは必ずしも良いことではありません、確かではありません。
「低メモリ状態」でビューがアンロードされていることを示すビットは、デフォルトでは残っていることを示しています。したがって、実際は親コントローラの実装に依存します。ナビゲーションコントローラを使用すると、毎回新しいインスタンスが作成され、ロードされます。タブコントローラーではそうではありません。 – Imran
低メモリ動作(シミュレータ上)をテストする方法は、ビューコントローラを配置し、モーダルビューコントローラでカバーし、ハードウェア - >メモリシミュレーション警告オプションを使用することです。非表示のコントローラーのビューはアンロードしてからモーダルモードを解除すると再読み込みする必要があります。興味深いことに、それを試してみてください。 –
私は現在アクティブではないビューは、アンロードするための候補だと思います。 – Imran