24

NSObject UIKit Additions Referenceによると、アウトレット変数は、awakeFromNib呼び出されるまでに設定する必要があります (すべて私のものを強調してください)。

nib 読み込みインフラストラクチャは、アーカイブ内のすべてのオブジェクトが読み込まれて初期化された後にのみ、nib アーカイブから再作成された各オブジェクトに awakeFromNib メッセージを送信します。オブジェクトが awakeFromNib メッセージを受信すると、すべてのアウトレットとアクション接続がすでに確立されていることが保証されます。

...

重要: オブジェクトがアーカイブからインスタンス化される順序は保証されていないため、初期化メソッドは階層内の他のオブジェクトにメッセージを送信しないでください。他のオブジェクトへのメッセージは、awakeFromNib メソッド内から安全に送信できます。

通常、設計時に実行できない追加の設定が必要なオブジェクトに対しては、awakeFromNib を実装します。たとえば、このメソッドを使用して、任意のコントロールの既定の構成をカスタマイズして、ユーザー設定や他のコントロールの値に一致させることができます。また、個々のコントロールをアプリケーションの以前の状態に復元するために使用することもできます。

ただし、少なくともストーリーボードを使用した場合、これは私のテストと一致しません。次のテストの結果は、ドキュメントと矛盾しているようです。

  • Xcode で新しいシングル ビュー アプリケーションを作成します。
  • 2 つ目の ViewController をストーリーボードにドラッグします。
  • 最初の ViewController にボタンを与え、そのボタンから 2 番目の ViewController を表示するモーダル セグエを作成します。
  • 2 番目の ViewController の ViewController クラス ファイルを作成します。
  • ストーリーボードの 2 番目の ViewController にラベルを作成し、someLabelそこから対応する ViewController クラスに呼び出されるアウトレットを作成します。
  • awakeFromNib次の実装を 2 番目の ViewController に追加します。

.

- (void) awakeFromNib {
    [super awakeFromNib];
    if (self.someLabel == nil) {
        NSLog(@"someLabel property is nil");
    }
    else {
        NSLog(@"someLabel property is not nil");
    }
    
    if (_someLabel == nil) {
        NSLog(@"_someLabel is nil");
    }
    else {
        NSLog(@"_someLabel is not nil");
    }
}
  • シミュレーターでアプリを実行し、ボタンをクリックします。

これを行うと、次のログが記録されます。

2013-07-01 09:24:35.755 test[498:c07] someLabel property is nil
2013-07-01 09:24:35.758 test[498:c07] _someLabel is nil

この動作の結果として、ViewController にアウトレットを含む初期化ロジックが必要な場合は、アウトレットを使用できるようにするために、ここでの回答で提案されているようなハックを使用する必要があります。ドキュメントを正しく理解していれば、このハックを使用せざるを得ないという事実はUIKitの動作のバグであり、その初期化を入れて、ハックなしでアウトレットを簡単に使用できるはずです。awakeFromNib

ただし、インターネット上でこの問題について他の言及を見つけることはできません. また、実際のnibファイルを使用したことはなく、ストーリーボードのみを使用したため、これについての視点が欠けています。このようなドキュメントは冗長で難しく、iOSの初心者として、理解したと確信できません.正しく。これは本物の UIKit のバグですか、それともドキュメントを何らかの形で誤解していますか? おそらく、この方法はストーリーボードと組み合わせて使用​​することを意図していませんか?

4

2 に答える 2

18

簡潔な答え

ビュー コントローラとそのビュー階層は、実行時に個別の nib ファイルからロードされます。

ビュー コントローラが最初にawakeFromNibロードされ、その nib がロードされたときに受け取りますが、そのビュー階層 nib はまだロードされていないためawakeFromNib、ビュー階層へのアウトレットがまだ設定されていると想定しないでください。

ビュー コントローラーはviewDidLoad、ビュー階層 nib が読み込まれた後に受信するため、viewDidLoadすべてのアウトレットが設定されていると想定できます。

長い答え

Xcode がアプリをビルドすると、ストーリーボードがコンパイルされます。その結果、1 つまたは複数のファイルを含むパッケージ (Finder がファイルとして扱うフォルダー)Info.plist.nib作成されます。私のプロジェクトの例:

:; pwd
/Users/mayoff/Library/<snip>/Pinner.app/Base.lproj/Main.storyboardc
:; ll
total 80
drwxr-xr-x  10 mayoff  staff   340 May 11 22:13 ./
drwxr-xr-x   4 mayoff  staff   136 May 11 22:13 ../
-rw-r--r--   1 mayoff  staff  1700 May 11 22:13 AccountCollection.nib
-rw-r--r--   1 mayoff  staff  1110 May 11 22:13 AccountEditor.nib
-rw-r--r--   1 mayoff  staff  2999 May 11 22:13 BYZ-38-t0r-view-8bC-Xf-vdC.nib
-rw-r--r--   1 mayoff  staff   439 May 11 22:13 Info.plist
-rw-r--r--   1 mayoff  staff  7621 May 11 22:13 LqH-9K-CeF-view-OwT-Ts-HoG.nib
-rw-r--r--   1 mayoff  staff  6570 May 11 22:13 OZq-QF-pn5-view-xSR-gK-reL.nib
-rw-r--r--   1 mayoff  staff  2473 May 11 22:13 UINavigationController-ZKB-z3-xgf.nib
-rw-r--r--   1 mayoff  staff   847 May 11 22:13 UIPageViewController-ufv-JN-y6U.nib

Info.plist、ストーリーボードのシーン名を対応する nib にマップします。

:; plutil -p Info.plist 
{
  "UIViewControllerIdentifiersToNibNames" => {
    "AccountCollection" => "AccountCollection"
    "UINavigationController-ZKB-z3-xgf" => "UINavigationController-ZKB-z3-xgf"
    "UIPageViewController-ufv-JN-y6U" => "UIPageViewController-ufv-JN-y6U"
    "AccountEditor" => "AccountEditor"
  }
  "UIStoryboardDesignatedEntryPointIdentifier" => "UINavigationController-ZKB-z3-xgf"
  "UIStoryboardVersion" => 1
}

シーンがこのリストに表示されるのは、シーンにストーリーボード ID がある場合、またはセグエがシーンに接続している場合、または最初のシーンである場合のみです。

の nib ファイル リストには、これらのビュー コントローラーのビュー階層は含まれInfo.plistていません。これらの各 nib ファイルには、そのシーンのビュー コントローラーとシーン内の他のトップレベル オブジェクトが含まれていますが、ビュー コントローラーのビューまたはそのサブビューは含まれていません。

別の nib ファイルには、シーンのビュー階層が含まれています。ビュー コントローラとその最上位ビューのオブジェクト ID から派生したビュー階層 nib の名前。Xcode の「Identity Inspector」で、ストーリーボード内の任意のオブジェクトのオブジェクト ID を確認できます。たとえば、私の「AccountCollection」シーンのビュー コントローラーの ID はBYZ-38-t0rで、そのビューの ID は8bC-Xf-vdCであるため、シーンのビュー階層は file にありますBYZ-38-t0r-view-8bC-Xf-vdC.nib。シーン nib ファイルには、そのビュー階層 nib ファイルの名前が含まれています。

:; strings - AccountCollection.nib |grep -e '-.*-'
UIPageViewController-ufv-JN-y6U
BYZ-38-t0r-view-8bC-Xf-vdC          <---------
UpstreamPlaceholder-5Hn-fK-fqQ
UpstreamPlaceholder-8GL-mk-Rao
q1g-aL-SLo.title

シーンにビュー階層がない場合、ビュー コントローラー用の nib ファイルだけが存在し、ビュー階層用の別の nib ファイルはありません。たとえば、UIPageViewControllerシーンにはストーリーボードにビュー階層がないため、に対応するビュー階層ニブはありませんUIPageViewController-ufv-JN-y6U.nib

それで、これはあなたの質問と何の関係がありますか?アプリが「ストーリーボード」からシーンをロードするとき、View Controller (およびその他のトップレベル オブジェクト) を含む nib ファイルをロードしています。nib ローダーがその nib ファイルのロードを完了すると、awakeFromNibロードしたばかりのすべてのオブジェクトに送信します。これにはView Controllerが含まれますが、ビューはそのnibファイルに含まれていなかったため、ビューは含まれません。

後で、View Controller がそのviewプロパティを求められると、ビュー階層を含む nib ファイルをロードします。ビュー コントローラは、自身を引数-[UINib instantiateWithOwner:options:]として渡します。ownerこれは、nib ローダーがビュー階層内のオブジェクトをビュー コントローラーのアウトレットとアクションに接続する方法です。

nib ローダーがビュー階層 nib のロードを完了すると、awakeFromNibロードしたばかりのすべてのオブジェクトに送信します。ビュー コントローラーはこれらのオブジェクトの 1 つではなかったため、現時点ではビュー コントローラーはメッセージを受信しません。awakeFromNib

制御がinstantiateWithOwner:options:戻ると、View Controller は自分自身にviewDidLoadメッセージを送信します。これは、ビュー階層を変更する機会です。

于 2016-05-12T15:29:08.413 に答える
4

ビューコントローラーは、ビューが実際にビューを作成するためにアクセスされるまで待機します。ボタンはView Controllerのビューにあるため、まだインスタンス化されません。

于 2013-07-01T09:06:58.333 に答える