2012-08-15 4 views
9

私は、コントローラのviewDidLoadメソッドがコントローラに初めてアクセスされたときに呼び出されるだけでなく、常に少なくとも一回。viewDidLoadは、実際にはセグのトランジションがあるたびに呼び出されます

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

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

私はこれと矛盾見つけることができるのviewDidLoadのすべての説明:メモリが少ないとき

そしてりんご自身の文書がビューにのみアンロードされることを示しているに。

私は現在viewDidLoadで初期化を行っていますが、これはすべてのsegueトランジションで呼び出されることを前提としています。

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

答えて

11

アップルのドキュメントでは、View Controllerの割り当てが解除されていない状況が説明されていると思います。セグを使用する場合は、新しい送り先コントローラのインスタンス化を引き起こしています。新しいオブジェクトであるため、ビューを読み込む必要があります。

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

編集: あなたが含まれているリンクを読むと、私はそれらに矛盾は見当たりません。彼らもまた、View Controllerオブジェクトの寿命中に起こることについて話しています。

+0

「低メモリ状態」でビューがアンロードされていることを示すビットは、デフォルトでは残っていることを示しています。したがって、実際は親コントローラの実装に依存します。ナビゲーションコントローラを使用すると、毎回新しいインスタンスが作成され、ロードされます。タブコントローラーではそうではありません。 – Imran

+1

低メモリ動作(シミュレータ上)をテストする方法は、ビューコントローラを配置し、モーダルビューコントローラでカバーし、ハードウェア - >メモリシミュレーション警告オプションを使用することです。非表示のコントローラーのビューはアンロードしてからモーダルモードを解除すると再読み込みする必要があります。興味深いことに、それを試してみてください。 –

+0

私は現在アクティブではないビューは、アンロードするための候補だと思います。 – Imran

0

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

12

フィリップミルズの答えは正しいです。これは単なるその拡張です。

システムは文書化されているとおりに動作しています。

ナビゲーションコントローラにプッシュされているビューコントローラがの新しいインスタンスであるため、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を使用している間、キャッシュされたコントローラを取得する方法です...しかし、それは必ずしも良いことではありません、確かではありません。

+0

詳細な回答ありがとうございます。コントローラをキャッシュしないでください。私はロード・アンロード・サイクルをもう少し理解したかったので、viewDidLoadにできることとできないことを正確に理解することができました。今ははるかに明確です。 – Imran

+1

実際、viewWillUnloadとviewDidUnloadは廃止されました。そのため、今後はコードを実際に置くべきではありません。 didReceiveMemoryWarningを処理してメモリ不足時にリソースを解放します。 –

+0

ああ、IOS6で右。もうviewDidLoadへの本当のポイントがないように思えます。それはコンストラクタでもあります。これは、メモリ警告がもはやviewDidUnloadを呼び出さないことを意味します。この種の変更は、viewDidLoadが一度だけ呼び出されることを意味します。私はIOS6をテストする瞬間がありません。 – Imran

関連する問題