概述
Objective-C作為一門面向對象語言鹃栽,有類和對象的概念。編譯后躯畴,類相關的數(shù)據(jù)結構會保留在目標文件中民鼓,在運行時得到解析和使用。在應用程序運行起來的時候蓬抄,類的信息會有加載和初始化過程丰嘉。
就像Application有生命周期回調方法一樣,在Objective-C的類被加載和初始化的時候嚷缭,也可以收到方法回調饮亏,可以在適當?shù)那闆r下做一些定制處理。而這正是load和initialize方法可以幫我們做到的峭状。
+ (void)load;
+ (void)initialize;
可以看到這兩個方法都是以“+”開頭的類方法克滴,返回為空。通常情況下优床,我們在開發(fā)過程中可能不必關注這兩個方法。如果有需要定制誓焦,我們可以在自定義的NSObject子類中給出這兩個方法的實現(xiàn)胆敞,這樣在類的加載和初始化過程中,自定義的方法可以得到調用杂伟。
+load
顧名思義移层,+load方法在這個文件被程序裝載時調用。只要是在Compile Sources中出現(xiàn)的文件總是會被裝載赫粥,這與這個類是否被用到無關观话,因此+load方法總是在main函數(shù)之前調用。
調用方式:
會循環(huán)調用所有類的 +load 方法越平。注意频蛔,這里是(調用分類的 +load 方法也是如此)直接使用函數(shù)內存地址的方式 (*load_method)(cls, SEL_load); 對 +load 方法進行調用的,而不是使用發(fā)送消息 objc_msgSend 的方式秦叛。
這樣的調用方式就使得 +load 方法擁有了一個非常有趣的特性晦溪,那就是子類、父類和分類中的 +load 方法的實現(xiàn)是被區(qū)別對待的挣跋。也就是說如果子類沒有實現(xiàn) +load 方法三圆,那么當它被加載時 runtime 是不會去調用父類的 +load 方法的。同理,當一個類和它的分類都實現(xiàn)了 +load 方法時舟肉,兩個方法都會被調用修噪。
要點:
- 調用時機比較早,運行環(huán)境有不確定因素路媚。具體說來黄琼,在iOS上通常就是App啟動時進行加載,但當load調用的時候磷籍,并不能保證所有類都加載完成且可用适荣,必要時還要自己負責做auto release處理。
補充上面一點院领,對于有依賴關系的兩個庫中弛矛,被依賴的類的+load會優(yōu)先調用。但在一個庫之內比然,父丈氓、子類、類別之間調用有順序强法,不同類之間調用順序是不確定的万俗。 - 關于繼承:對于一個類而言,沒有+load方法實現(xiàn)就不會調用饮怯,不會考慮對NSObject的繼承闰歪,就是不會沿用父類的+load。
- 父類和本類的調用:父類的方法優(yōu)先于子類的方法蓖墅。一個類的+load方法不用寫明[super load]库倘,父類就會收到調用。
- 本類和Category的調用:本類的方法優(yōu)先于類別(Category)中的方法论矾。Category的+load也會收到調用教翩,但順序上在本類的+load調用之后。
- 不會直接觸發(fā)initialize的調用贪壳。
+initialize
+initialize 方法是在類或它的子類收到第一條消息之前被調用的饱亿,這里所指的消息包括實例方法和類方法的調用,并且只會調用一次闰靴。initialize方法實際上是一種惰性調用彪笼,也就是說如果一個類一直沒被用到,那它的initialize方法也不會被調用传黄,這一點有利于節(jié)約資源杰扫。
調用方式:
runtime 使用了發(fā)送消息 objc_msgSend 的方式對 +initialize 方法進行調用。也就是說 +initialize 方法的調用與普通方法的調用是一樣的膘掰,走的都是發(fā)送消息的流程章姓。換言之佳遣,如果子類沒有實現(xiàn) +initialize 方法,那么繼承自父類的實現(xiàn)會被調用凡伊;如果一個類的分類實現(xiàn)了 +initialize 方法零渐,那么就會對這個類中的實現(xiàn)造成覆蓋。
要點:
- initialize的自然調用是在第一次主動使用當前類的時候系忙。
- 在initialize方法收到調用時诵盼,運行環(huán)境基本健全。
- 關于繼承:和load不同银还,即使子類不實現(xiàn)initialize方法风宁,會把父類的實現(xiàn)繼承過來調用一遍,就是會沿用父類的+initialize蛹疯。(沿用父類的方法中戒财,self還是指子類)
- 父類和本類的調用:子類的+initialize將要調用時會激發(fā)父類調用的+initialize方法,所以也不需要在子類寫明[super initialize]捺弦。(本著除主動調用外饮寞,只會調用一次的原則,如果父類的+initialize方法調用過了列吼,則不會再調用)
- 本類和Category的調用:Category中的+initialize方法會覆蓋本類的方法幽崩,只執(zhí)行一個Category的+initialize方法。
類別(Category)
對于+initialize寞钥,只有最后一個類別執(zhí)行慌申,本類的+initialize和前面類別的+initialize被隱藏。
而對于+load理郑,本類和本類的所有類別都執(zhí)行太示,并且如果Apple的文檔中介紹順序一樣:先執(zhí)行類自身的實現(xiàn),再執(zhí)行類別中的實現(xiàn)香浩。
擴展
因為兩個方法只會被系統(tǒng)調用一次(除主動調用外),并且是線程安全的臼勉,可以用來作為單例的實現(xiàn)邻吭。(可以用+initialize,+load有些隱患宴霸,看這里)
?注意
- 在使用時都不要過重地依賴于這兩個方法囱晴,除非真正必要。
- 謹慎在分類中實現(xiàn)+initialize方法瓢谢,因為如果在分類中實現(xiàn)了畸写,本類實現(xiàn)的+initialize方法將不會被調用。
- 謹慎在分類中實現(xiàn)+load方法氓扛。因為如果在本類中實現(xiàn)+load方法混淆A枯芬、B兩個方法论笔,分類中也混淆A、B千所,因為本類和分類的+load都實現(xiàn)了狂魔,所以都會調用,A淫痰、B在本類中置換后最楷,又在分類中置換了回來。
- load方法通常用來進行Method Swizzle待错,initialize方法一般用于初始化全局變量或靜態(tài)變量籽孙。
- load和initialize方法內部使用了鎖,因此它們是線程安全的火俄。實現(xiàn)時要盡可能保持簡單犯建,避免阻塞線程,不要再使用鎖烛占。
問題
問題:
- 子類胎挎、父類、分類中的相應方法什么時候會被調用忆家?
- 需不需要在子類的實現(xiàn)中顯式地調用父類的實現(xiàn)犹菇?
解答:
- super的方法會成功調用,但是這是多余的芽卿,因為runtime會自動對父類的+load方法進行調用揭芍,而+initialize則會隨子類自動激發(fā)父類的方法(如Apple文檔中所言)不需要顯示調用。另一方面卸例,如果父類中的方法用到的self(像示例中的方法)称杨,其指代的依然是類自身,而不是父類筷转。
總結
+load | +initialize | |
---|---|---|
調用時機 | 被添加到 runtime 時 | 到第一條消息前姑原,可能永遠不調用 |
同一個類,調用次數(shù) (不考慮主動調用) |
1次 | 1次 |
調用順序 | 父類->本類->分類 | 父類->本類(如果有分類呜舒,則調用分類) |
若自身未實現(xiàn)锭汛,是否沿用父類的方法? | 否 | 是 |
類別中的定義 | 全都執(zhí)行袭蝗,但后于本類的方法 | 覆蓋本類的方法唤殴,只執(zhí)行一個 |
線程安全 | 安全 | 安全 |
結束語
參考
-
Objective-C +load vs +initialize
文章根據(jù)runtime源碼來解釋+load和+initialize特性。