Phillip Mills の答えは正しいです。これは単なる拡張です。
システムは説明どおりに機能しています。
ナビゲーション コントローラーにプッシュされるビュー コントローラーは新しいインスタンスであるため、viewDidLoad が表示されます。viewDidLoad を呼び出す必要があります。
もう少し詳しく調べると、これらのビュー コントローラーがポップされたときに割り当てが解除されていることがわかります (dealloc にブレークポイントまたは NSLog を配置するだけです)。この割り当て解除は、ビュー コントローラー コンテナーとは何の関係もありません...使用するコントローラーの寿命を制御するものではありません...それへの強い参照を保持しているだけです。
コントローラがナビゲーション コントローラ スタックから取り出されると、ナビゲーション コントローラはその参照を解放します。他の参照がないため、ビュー コントローラは割り当てを解除します。
ナビゲーション コントローラーは、アクティブなスタックにあるビュー コントローラーへの強い参照のみを保持します。
同じコントローラーを再利用する場合は、再利用する責任があります。ストーリーボード セグエを使用すると、そのコントロールを (大部分) 放棄します。
いくつかのボタンをタップした結果として、 View Controller へのpush
セグエがあるとします。Foo
そのボタンがタップされると、「システム」はFoo
(宛先ビュー コントローラー) のインスタンスを作成し、セグエを実行します。コントローラー コンテナーは、そのビュー コントローラーへの唯一の強力な参照を保持するようになりました。それが完了すると、VC は割り当てを解除します。
毎回新しいコントローラーを作成するため、viewDidLoad
そのコントローラーが提示されるたびに呼び出されます。
ここで、この動作を変更し、後で再利用するために View Controller をキャッシュする場合は、具体的に行う必要があります。ストーリーボード セグエを使用しない場合は、実際に VC をナビゲーション コントローラーにプッシュ/ポップするので簡単です。
ただし、ストーリーボード セグエを使用する場合は、もう少し面倒です。
いくつかの方法がありますが、いずれもなんらかのハッキングが必要です。ストーリーボード自体が、新しいビュー コントローラーのインスタンス化を担当します。1 つの方法は、オーバーライドすることinstantiateViewControllerWithIdentifier
です。これは、セグエがビュー コントローラーを作成する必要があるときに呼び出されるメソッドです。識別子を与えていないコントローラに対しても呼び出されます (割り当てない場合、システムは独自の識別子を提供します)。
これが主に教育目的であることを願っています。問題が何であれ、これが問題を解決するための最良の方法であるとは決して提案していません。
何かのようなもの...
@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 を公開していません。ただし、各View Controllerには、storyboard
作成元のストーリーボードを取得するためのメソッドがあります。
独自のストーリーボードをロードしている場合は、MyStoryboard をインスタンス化するだけです。デフォルトのストーリーボードを使用している場合は、システムに特別なものを強制的に使用させる必要があります。繰り返しますが、これを行う方法はたくさんあります。簡単な方法の 1 つは、ビュー コントローラーでストーリーボード アクセサー メソッドをオーバーライドすることです。
MyStoryboard をすべてを UIStoryboard に転送するプロキシ クラスにするか、メイン ストーリーボードを isa-swizzle にするか、ローカル コントローラーにストーリーボード メソッドから 1 つを返すようにすることができます。
ここで問題があることを思い出してください。同じView Controllerをスタックに2回以上プッシュするとどうなりますか? キャッシュを使用すると、まったく同じView Controllerオブジェクトが複数回使用されます。それは本当にあなたが望むものですか?
そうでない場合は、コントローラーコンテナー自体との対話を管理して、このコントローラーが既に認識されているかどうかを確認できるようにする必要があります。その場合、新しいインスタンスが必要です。
したがって、デフォルトのストーリーボード セグエを使用しているときにキャッシュされたコントローラーを取得する方法があります (実際にはかなりの数の方法があります)。