類方法load和initialize的區別

Objective-C做爲一門面向對象語言,有類和對象的概念。編譯後,類相關的數據結構會保留在目標文件中,在運行時獲得解析和使用。在應用程序運行起來的時候,類的信息會有加載和初始化過程。
就像Application有生命週期回調方法同樣,在Objective-C的類被加載和初始化的時候,也能夠收到方法回調,能夠在適當的狀況下作一些定製處理。而這正是load和initialize方法能夠幫咱們作到的。安全

  • (void)load;
  • (void)initialize;

能夠看到這兩個方法都是以「+」開頭的類方法,返回爲空。一般狀況下,咱們在開發過程當中可能沒必要關注這兩個方法。若是有須要定製,咱們能夠在自定義的NSObject子類中給出這兩個方法的實現,這樣在類的加載和初始化過程當中,自定義的方法能夠獲得調用。
+load數據結構

顧名思義,+load方法在這個文件被程序裝載時調用。只要是在Compile Sources中出現的文件老是會被裝載,這與這個類是否被用到無關,所以+load方法老是在main函數以前調用。
調用方式:
會循環調用全部類的 +load 方法。注意,這裏是(調用分類的 +load 方法也是如此)直接使用函數內存地址的方式 (*load_method)(cls, SEL_load); 對 +load 方法進行調用的,而不是使用發送消息 objc_msgSend 的方式。
這樣的調用方式就使得 +load 方法擁有了一個很是有趣的特性,那就是子類、父類和分類中的 +load 方法的實現是被區別對待的。也就是說若是子類沒有實現 +load 方法,那麼當它被加載時 runtime 是不會去調用父類的 +load 方法的。同理,當一個類和它的分類都實現了 +load 方法時,兩個方法都會被調用。
要點:函數

  • 調用時機比較早,運行環境有不肯定因素。具體說來,在iOS上一般就是App啓動時進行加載,但當load調用的時候,並不能保證全部類都加載完成且可用,必要時還要本身負責作auto release處理。

補充上面一點,對於有依賴關係的兩個庫中,被依賴的類的+load會優先調用。但在一個庫以內,父、子類、類別之間調用有順序,不一樣類之間調用順序是不肯定的。spa

  • 關於繼承:對於一個類而言,沒有+load方法實現就不會調用,不會考慮對NSObject的繼承,就是不會沿用父類的+load。
  • 父類和本類的調用:父類的方法優先於子類的方法。一個類的+load方法不用寫明[super load],父類就會收到調用。
  • 本類和Category的調用:本類的方法優先於類別(Category)中的方法。Category的+load也會收到調用,但順序上在本類的+load調用以後。
  • 不會直接觸發initialize的調用。

+initialize線程

+initialize 方法是在類或它的子類收到第一條消息以前被調用的,這裏所指的消息包括實例方法和類方法的調用,而且只會調用一次。initialize方法其實是一種惰性調用,也就是說若是一個類一直沒被用到,那它的initialize方法也不會被調用,這一點有利於節約資源。
調用方式:
runtime 使用了發送消息 objc_msgSend 的方式對 +initialize 方法進行調用。也就是說 +initialize 方法的調用與普通方法的調用是同樣的,走的都是發送消息的流程。換言之,若是子類沒有實現 +initialize 方法,那麼繼承自父類的實現會被調用;若是一個類的分類實現了 +initialize 方法,那麼就會對這個類中的實現形成覆蓋。
要點:對象

  • initialize的天然調用是在第一次主動使用當前類的時候。
  • 在initialize方法收到調用時,運行環境基本健全。
  • 關於繼承:和load不一樣,即便子類不實現initialize方法,會把父類的實現繼承過來調用一遍,就是會沿用父類的+initialize。(沿用父類的方法中,self仍是指子類)
  • 父類和本類的調用:子類的+initialize將要調用時會激發父類調用的+initialize方法,因此也不須要在子類寫明[super initialize]。(本着除主動調用外,只會調用一次的原則,若是父類的+initialize方法調用過了,則不會再調用)
  • 本類和Category的調用:Category中的+initialize方法會覆蓋本類的方法,只執行一個Category的+initialize方法。

類別(Category)繼承

對於+initialize,只有最後一個類別執行,本類的+initialize和前面類別的+initialize被隱藏。
而對於+load,本類和本類的全部類別都執行,而且若是Apple的文檔中介紹順序同樣:先執行類自身的實現,再執行類別中的實現。
擴展生命週期

由於兩個方法只會被系統調用一次(除主動調用外),而且是線程安全的,能夠用來做爲單例的實現。(能夠用+initialize,+load有些隱患,看這裏)
�注意ip

  • 在使用時都不要太重地依賴於這兩個方法,除非真正必要。
  • 謹慎在分類中實現+initialize方法,由於若是在分類中實現了,本類實現的+initialize方法將不會被調用。
  • 謹慎在分類中實現+load方法。由於若是在本類中實現+load方法混淆A、B兩個方法,分類中也混淆A、B,由於本類和分類的+load都實現了,因此都會調用,A、B在本類中置換後,又在分類中置換了回來。
  • load方法一般用來進行Method Swizzle,initialize方法通常用於初始化全局變量或靜態變量。
  • load和initialize方法內部使用了鎖,所以它們是線程安全的。實現時要儘量保持簡單,避免阻塞線程,不要再使用鎖。

問題內存

問題:

  1. 子類、父類、分類中的相應方法何時會被調用?
  2. 需不須要在子類的實現中顯式地調用父類的實現?

解答:

  1. super的方法會成功調用,可是這是多餘的,由於runtime會自動對父類的+load方法進行調用,而+initialize則會隨子類自動激發父類的方法(如Apple文檔中所言)不須要顯示調用。另外一方面,若是父類中的方法用到的self(像示例中的方法),其指代的依然是類自身,而不是父類。

總結

clipboard.png

相關文章
相關標籤/搜索