11

コントローラーのviewDidLoadメソッドは、コントローラーが最初にアクセスされたときにのみ呼び出され、必ずしも毎回ではなく、少なくとも1回は呼び出されるというスタックオーバーフローに関する多くの投稿を見てきました。

これは私が見ているものではありません!これを強調するために簡単なテストをまとめました: https://github.com/imuz/ViewDidLoadTest

ナビゲーションコントローラーのセグエとモーダルビューのようです viewDidLoad は常に呼び出されます。呼び出されないのは、タブを切り替えるときだけです。

私が見つけることができるviewDidLoadのすべての説明は、これと矛盾しています:

また、リンゴ自身のドキュメントは、メモリが少ない場合にのみビューがアンロードされることを示しています。

私は現在、viewDidLoad で初期化を行っており、すべてのセグエ遷移で呼び出されることを前提としています。

ここで何か不足していますか?

4

3 に答える 3

14

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オブジェクトが複数回使用されます。それは本当にあなたが望むものですか?

そうでない場合は、コントローラーコンテナー自体との対話を管理して、このコントローラーが既に認識されているかどうかを確認できるようにする必要があります。その場合、新しいインスタンスが必要です。

したがって、デフォルトのストーリーボード セグエを使用しているときにキャッシュされたコントローラーを取得する方法があります (実際にはかなりの数の方法があります)。

于 2012-08-15T15:35:24.000 に答える
13

Apple のドキュメントには、View Controller の割り当てが解除されていない状況が記載されていると思います。セグエを使用すると、新しい宛先コントローラーのインスタンス化が発生し、新しいオブジェクトであるため、ビューをロードする必要があります。

xib ベースのアプリでは、頻繁に再利用する可能性があるとわかっているコントローラー オブジェクトをキャッシュすることがあります。そのような場合、ビューをいつロードする必要があるかという点で、ドキュメントに沿って動作しました。

編集:あなたが含めたリンクを読んでも、それらに矛盾は見られません。彼らもまた、View Controller オブジェクトの存続期間中に起こることについて話しているのです。

于 2012-08-15T12:50:55.953 に答える
0

コントローラーのビューが最初から読み込まれるたびに呼び出されます (つまり、要求されたがまだ利用できない)。コントローラーの割り当てを解除し、ビューがそれに付随する場合、次にコントローラーをインスタンス化するときに再度呼び出されます (たとえば、コントローラーを作成してモーダルまたはセグエ経由でプッシュする場合)。タブ内のビュー コントローラーは、タブ コントローラーによって保持されるため、割り当てが解除されません。

于 2012-08-15T12:50:37.870 に答える