loadView / viewDidLoad / awakeFromNib / initWithCoder 總結


     每個 iOS 開發者對 loadView 和 viewDidLoad 肯定都很熟悉,雖然這兩個函數使用上真的是非常簡單,但是和類似的 initWithNibName: / awakeFromNib / initWithCoder: 放在一起還是非常容易讓人混淆的。


一、loadView

      永遠不要主動調用這個函數。view controller 會在 view 的 property 被請求並且當前 view 值爲 nil 時調用這個函數。如果你手動創建 view,你應該重載這個函數, 且不要在重載的時候調用 [super loadview]。如果你用 IB 創建 view 並初始化 view controller,那就意味着你使用 initWithNibName:bundle: 方法,這時你不應該重載 loadView 函數。

       這個方法系統的默認實現是這樣:

       1. 尋找有關可用的 nib 文件的信息,根據這個信息來加載 nib 文件       // 所以,nib 的加載過程是在 loadview 中完成的哦。

       2. 如果沒有有關 nib 文件的信息,默認創建一個空白的 UIView 對象,然後把對象成賦值給 viewcontroller 的主 view。

      所以,如果你決定重載這個函數時,你也應該完成這些步驟:

      把子類的 view 賦給 view 屬性(property)(你 create 的 view 必須是唯一的實例,並且不被其他任何 controller 共享),而且你重載的這個函數不應該調用 super,這個也是爲了保持主 view 與 controller 的單一映射關係。


二、viewDidLoad

      這個函數在 controller 加載了相關的 views 後被調用,而不論這些 views 存儲在 nib 文件裏還是在 loadView 函數中生成。

      這個函數的作用主要是讓你可以進一步的初始化你的 views。viewDidLoad 通常負責的是 view 及其子 view 被加載進內存之後的數據初始化的工作,即視圖的數據部分的初始化。在 iOS 3.0 以及更高版本中,你應該重載 viewDidUnload 函數來釋放任何對 view 的引用或者它裏面的內容(子 view 等等)。

      其多數情況下是做 nib 文件的後續工作。


三、viewDidUnload

      iOS6 之前這個函數是 viewDidLoad 的對立函數。在程序內存欠缺時,這個函數被 controller 調用,來釋放它的 view 以及 view 相關的對象。通常情況下,未顯示在界面的 ViewController 是 UINavigationController Push 棧中未在棧頂的 ViewController,以及 UITabBarController 中未顯示的子 ViewController。這些 ViewController 都在 MemoryWarning 事件發生時,讓系統自動調用 viewDidUnload 方法。由於 controller 通常保存着 view 以及相關 object 的引用,所以你必須使用這個函數來放棄這些對象的所有權以便內存回收。但不要釋放那些難以重建的數據。

      通常 controller 會保存 nib 文件建立的 views 的引用,但是也可能會保存着 loadView 函數創建的對象的引用。最完美的方法是使用合成器方法:

self.customSubview = nil;

      這樣合成器會 release 這個 view,如果你沒有使用 property,那麼你得自己顯式釋放這個 view。


      但是 iOS6 之後,,由於viewDidUnload 事件任何情況下都不會被觸發,所以蘋果在文檔中建議,應該將回收內存的相關操作移到另一個函數 didReceiveMemoryWarning 中。但是如果僅僅寫成:(以下爲錯誤示例)

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];


    if (self.isViewLoaded && !self.view.window) {
        self.view=nil;
    }
}


       在 iOS6 以後,不建議將 view 置爲 nil 的原因如下:
       1. UIView 有一個 CALayer 成員變量,CALayer 用於將自己畫到屏幕上:
       2. CALayer 是一個 bitmap 圖像的容器類,當 UIView 調用自身的 drawRect 時,CALayer 纔會創建 bitmap 圖像類。
       3. 具體佔內存的其實是一個 bitmap 圖像類,CALayer 只佔 48Bytes,UiView 只佔 96Bytes。而一個 iPad 的全屏 UIView 的bitmap 類會佔到 12MB 的大小。
       4. 在 iOS6,當系統發出 MemoryWarning 時,系統會自動回收 bitmap 類,但是不回收 UIView 和 CALayer 類。這樣既能回收大部分內存,又能在需要 bitmap 類時,通過調用 UIView 的 drawRect: 方法重建。


       內存優化:
       蘋果系統對上面的內存回收做了一個優化:
       1. 當一段內存被分配時,它會被標記成 “In Use”以防止被重複使用。當內存被釋放時,這段內存被標記爲 “Not in use”,這樣有新的內存申請時,這塊內存就可能被分配給其他變量。
       2. CALayer 包括的具體的 bitmap 內容的私有成員變量類型爲 CABackingStore,當收到 MemoryWarning 時,CABackingStore 類型的內存會被標記爲 Volatile 類型,表示這塊內存可能再次被原變量使用。


       這樣,有了上面優化後,當收到 MemoryWarning 時,雖然所有的 CALayer 所包含的 bitmap 內存被標記成 volatile 了,但是隻要這塊內存沒有被複用,當需要重建 bitmap 內存時,可以直接被複用,避免了再次調用 UIView 的 drawRect:方法。
       簡單說:iOS6 之後,不需要做任何以前 viewDidUnload 的事情,更不需要把以前 viewDidUnload 的代碼移到didReceiveMemoryWarning 方法中。


四、initWithCoder:

      當使用了 IB 創建對象,並實例化的時候會調用。

      使用 IB 創建對象,其實就是寫了 xib 或者 sb 文件(XML文件),那麼反序列化的工作是由系統完成的。


五、awakeFromNib

      IB 創建的對象被實例化的時候調用。

      當 initWithCoder: 被調用之後,也會調用 awakeFromNib。但是 awakeFromNib 相對於 initWithCoder: 有個優勢,後者在調用時雖然 subViews 已經被添加到圖層中去了,但是還沒有引用。而在 awakeFromNib 調用時,各種 IBOutlet 也都連接好了。


六、結論

      所以流程應該是這樣:

      loadView 來加載 view (無論nib文件或自定義的views)到內存 ——> viewDidLoad 函數進一步初始化這些view (通常是側重於數據 data 的初始化) ——> 內存不足時,調用 viewDidUnload 函數釋放 views ——> 當需要使用 view 時又回到第一步,如此循環

      需要理清兩個概念,創建一個類和實例化一個類。在 Xcode 中創建一個類和實例化一個類很容易區分,但是在 IB(Interface Builder) 中有時候就會迷糊。其實也很好區分,孤零零地創建了一個nib文件,沒有和其他可被實例化的類有直接或間接關係的時候,這個類或這些類(一個 nib 文件也可能包含多個類)是沒有機會被實例化的,所以這種情況只是通過 IB 創建了一個類,而沒有實例化。真正的實例化還需要通過在 Xcode 用代碼來讀取這個 nib 文件。知道這兩者的區別後這些方法也就容易辨認多了。

      loadView 需要分兩種情況。當你通過 Xcode 實例化一個類的時候就需要自己在 controller 中實現這個方法。而在 IB 中實例化就不需要實現它。

      viewDidLoad 其實沒什麼可混淆的,無論通過什麼途徑加載(Xcode 或者 IB,這裏的加載屬於實例化)完 view 對象到內存後肯定會執行這個方法。

      initWithNibName: 這個方法是在 controller 的類在 IB 中創建,但是通過 Xcode 實例化 controller 的時候用的。

      initWithCoder 是一個類在 IB 中創建但在 Xcode 中被實例化時被調用的。比如,通過 IB 創建一個 controller 的 nib 文件,然後在 Xcode 中通過 initWithNibName 來實例化這個 controller,那麼這個 controller 的 initWithCoder 會被調用。

      awakeFromNib 當 .nib 文件被加載的時候,會發送一個 awakeFromNib 的消息到 .nib 文件中的每個對象,每個對象都可以定義自己的 awakeFromNib 函數來響應這個消息,執行一些必要的操作。也就是說通過 nib 文件創建 view 對象時執行 awakeFromNib。awakeFromNib 在 loadView 前被調用。


七、補充

      在 IB 上創建類和實例化類的區分。IB 上實例化需要 Xcode 通過代碼來讀取這個 nib 文件的時候纔會實例化。所以有些時候,我們創建了一個 xib 文件,裏面有些控件,比如 UIButton,在沒有被顯示之前,這個 UIButton 可能只是一個 nil 指針。所以可以在viewWillAppear: 裏對這些控件屬性進行設置。
      當使用一個 controller 控制多個 nib 文件時,awakeFromNib 方法會被多次調用。因此,當不使用 awakeFromNib 方法來完成 nib 對象的初始化時,需要注意此方法的多次調用對其他 nib 文件造成的影響。


八、蘋果接口文檔

 

- (void)loadView

Description Creates the view that the controller manages.

You should never call this method directly. The view controller calls this method when its view property is requested but is currently nil. This method loads or creates a view and assigns it to the view property.

If the view controller has an associated nib file, this method loads the view from the nib file. A view controller has an associated nib file if the nibName property returns a non-nil value, which occurs if the view controller was instantiated from a storyboard, if you explicitly assigned it a nib file using the initWithNibName:bundle: method, or if iOS finds a nib file in the app bundle with a name based on the view controller'€™s class name. If the view controller does not have an associated nib file, this method creates a plain UIView object instead.

If you use Interface Builder to create your views and initialize the view controller, you must not override this method.

You can override this method in order to create your views manually. If you choose to do so, assign the root view of your view hierarchy to the view property. The views you create should be unique instances and should not be shared with any other view controller object. Your custom implementation of this method should not call super.

If you want to perform any additional initialization of your views, do so in the viewDidLoad method.

 

 

- (void)viewDidLoad

Description Called after the controller'€™s view is loaded into memory.

This method is called after the view controller has loaded its view hierarchy into memory. This method is called regardless of whether the view hierarchy was loaded from a nib file or created programmatically in the loadView method. You usually override this method to perform additional initialization on views that were loaded from nib files.

 


- (void)awakeFromNib

Description Prepares the receiver for service after it has been loaded from an Interface Builder archive, or nib file.

The nib-loading infrastructure sends an awakeFromNib message to each object recreated from a nib archive, but only after all the objects in the archive have been loaded and initialized. When an object receives an awakeFromNib message, it is guaranteed to have all its outlet and action connections already established.

You must call the super implementation of awakeFromNib to give parent classes the opportunity to perform any additional initialization they require. Although the default implementation of this method does nothing, many UIKit classes provide non-empty implementations. You may call the super implementation at any point during your own awakeFromNib method.

Note:Note

During Interface Builder’s test mode, this message is also sent to objects instantiated from loaded Interface Builder plug-ins. Because plug-ins link against the framework containing the object definition code, Interface Builder is able to call their awakeFromNib method when present. The same is not true for custom objects that you create for your Xcode projects. Interface Builder knows only about the defined outlets and actions of those objects; it does not have access to the actual code for them.

During the instantiation process, each object in the archive is unarchived and then initialized with the method befitting its type. Objects that conform to the NSCoding protocol (including all subclasses of UIView and UIViewController) are initialized using their initWithCoder: method. All objects that do not conform to the NSCoding protocol are initialized using their init method. After all objects have been instantiated and initialized, the nib-loading code reestablishes the outlet and action connections for all of those objects. It then calls the awakeFromNib method of the objects. For more detailed information about the steps followed during the nib-loading process, see Nib Files in Resource Programming Guide.

Important:Important

Because the order in which objects are instantiated from an archive is not guaranteed, your initialization methods should not send messages to other objects in the hierarchy. Messages to other objects can be sent safely from within an awakeFromNib method.

Typically, you implement awakeFromNib for objects that require additional set up that cannot be done at design time. For example, you might use this method to customize the default configuration of any controls to match user preferences or the values in other controls. You might also use it to restore individual controls to some previous state of your application.






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章