簡體   English   中英

awakeFromNib,outlet和storyboards:文檔錯了嗎?

[英]awakeFromNib, outlets and storyboards: is the documentation wrong?

根據NSObject UIKit Additions Reference ,應該在調用awakeFromNib設置出口變量(強調所有我的):

nib加載基礎結構將awakeFromNib消息發送到從nib歸檔重新創建的每個對象,但只有在歸檔中的所有對象都已加載並初始化之后。 當一個對象收到一個awakeFromNib消息時,它保證已經建立了所有的插座和動作連接。

...

要點:由於無法保證從歸檔實例化對象的順序,因此初始化方法不應將消息發送到層次結構中的其他對象。 可以從awakeFromNib方法中安全地發送到其他對象的消息。

通常,您需要為需要在設計時無法完成的其他設置的對象實現awakeFromNib。 例如,您可以使用此方法自定義任何控件的默認配置,以匹配用戶首選項或其他控件中的值。 您還可以使用它將單個控件還原到應用程序的某個先前狀態。

但是,這與我的測試不符,至少使用Storyboard。 以下測試的結果似乎與文檔相矛盾:

  • 在Xcode中創建一個新的單視圖應用程序。
  • 將第二個ViewController拖到故事板上。
  • 為第一個ViewController提供一個按鈕,並從該按鈕創建一個模式segue,顯示第二個ViewController。
  • 為第二個ViewController創建一個ViewController類文件。
  • 在故事板上的第二個ViewController上創建一個標簽,並從它創建一個名為someLabel的插座到相應的ViewController類。
  • 將以下awakeFromNib實現添加到第二個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

由於這種行為的后果之一,當我需要我的ViewControllers有涉及它們的出口一些初始化的邏輯,我需要使用像在回答提出的一個黑客在這里 ,為了能夠使用的插座。 如果我正確理解文檔,我被迫使用這個hack的事實是UIKit行為中的一個錯誤,我應該能夠將該初始化放在awakeFromNib並且只需使用沒有任何黑客的插座。

我在互聯網上找不到任何其他關於這個問題的提及,這看起來很奇怪,因為這對我來說是一個至關重要的功能。 我也從未使用過實際的nib文件,只使用了故事板,所以我對此缺少一些看法,關於這些內容的文檔冗長而且很難,作為iOS的新手,我不相信我已經理解了正確。 這是一個真正的UIKit錯誤,還是我以某種方式誤解了文檔 - 也許這種方法甚至不打算與故事板一起使用?

簡答

視圖控制器及其視圖層次結構在運行時從單獨的nib文件加載。

首先加載視圖控制器並在加載其nib時接收awakeFromNib ,但其視圖層次結構nib尚未加載,因此在awakeFromNib您不應假設已設置任何視圖層次結構的出口。

視圖控制器在加載視圖層次結構nib后接收viewDidLoad ,因此在viewDidLoad您可以假設已設置所有出口。

答案很長

當Xcode構建您的應用程序時,它會編譯您的故事板。 結果是一個包(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,或者segue連接到場景,或者它是初始場景,則場景僅顯示在此列表中。

Info.plist的nib文件列表包含這些視圖控制器的視圖層次結構。 每個nib文件都包含其場景的視圖控制器和場景中的任何其他頂級對象,但不包含視圖控制器的視圖或其任何子視圖。

單獨的nib文件包含場景的視圖層次結構。 視圖層次結構nib的名稱派生自視圖控制器的對象ID及其頂級視圖。 您可以在Xcode的“Identity Inspector”中查看故事板中任何對象的對象ID。 例如,我的“AccountCollection”場景的視圖控制器的ID是BYZ-38-t0r ,其視圖的ID是8bC-Xf-vdC ,因此場景的視圖層次結構位於文件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對應的視圖層次結構nib。

那么這與你的問題有什么關系呢? 以下是:當您的應用程序從“故事板”加載場景時,它正在加載包含視圖控制器(和其他頂級對象)的nib文件。 當nib加載器完成加載該nib文件時,它會將awakeFromNib發送到剛剛加載的所有對象。 這包括你的視圖控制器,但它包括你的意見,因為你的意見在筆尖文件沒有。

稍后,當您的視圖控制器被要求提供其view屬性時,它會加載包含其視圖層次結構的nib文件。 視圖控制器將自身傳遞給-[UINib instantiateWithOwner:options:]作為owner參數。 這就是nib加載器如何將視圖層次結構中的對象連接到視圖控制器的出口和操作。

當nib加載器完成加載視圖層次結構nib時,它會將awakeFromNib發送到剛剛加載的所有對象。 由於您的視圖控制器不是一個這樣的對象,你的視圖控制器收到awakeFromNib此時消息。

instantiateWithOwner:options:返回時,視圖控制器自己發送viewDidLoad消息。 這是您更改視圖層次結構的機會。

視圖控制器等到訪問其視圖以實際創建其視圖。 由於該按鈕位於視圖控制器的視圖中,因此它不會被實例化。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM