iOS開發(fā)中總能看到+load和+initialize的身影,網(wǎng)上對(duì)于這兩個(gè)方法有很多解釋,官方也有說明,但有些細(xì)節(jié)不夠清楚,今天我們來詳細(xì)扒一扒這兩個(gè)方法.
load
Apple文檔是這樣描述的
Invoked whenever aclassorcategoryisaddedtotheObjective-Cruntime;implement this method to performclass-specificbehavioruponloading.當(dāng)類(Class)或者類別(Category)加入Runtime中時(shí)(就是被引用的時(shí)候)。實(shí)現(xiàn)該方法,可以在加載時(shí)做一些類特有的操作。DiscussionThe load message is sent to classesandcategories that are both dynamically loadedandstatically linked, but onlyifthe newly loadedclassorcategoryimplementsamethodthatcanrespond.The order of initialization is asfollows:All initializersinany framework you link to.調(diào)用所有的Framework中的初始化方法All +load methodsinyour image.調(diào)用所有的+load方法All C++ static initializersandC/C++ attribute(constructor) functionsinyour image.調(diào)用C++的靜態(tài)初始化方及C/C++中的attribute(constructor)函數(shù)All initializersinframeworks that link to you.調(diào)用所有鏈接到目標(biāo)文件的framework中的初始化方法Inaddition:Aclass’s+loadmethodiscalledafterallofitssuperclasses’ +loadmethods.一個(gè)類的+load方法在其父類的+load方法后調(diào)用A category +load method is called after theclass’sown+loadmethod.一個(gè)Category的+load方法在被其擴(kuò)展的類的自有+load方法后調(diào)用In a custom implementation of load you can therefore safely message other unrelated classes from the same image, but any load methods implemented by those classes maynothave run yet.在+load方法中,可以安全地向同一二進(jìn)制包中的其它無關(guān)的類發(fā)送消息,但接收消息的類中的+load方法可能尚未被調(diào)用。
文檔地址:https://developer.apple.com/reference/objectivec/nsobject/1418815-load?language=objc
load函數(shù)調(diào)用特點(diǎn)如下:
當(dāng)類被引用進(jìn)項(xiàng)目的時(shí)候就會(huì)執(zhí)行l(wèi)oad函數(shù)(在main函數(shù)開始執(zhí)行之前),與這個(gè)類是否被用到無關(guān),每個(gè)類的load函數(shù)只會(huì)自動(dòng)調(diào)用一次.由于load函數(shù)是系統(tǒng)自動(dòng)加載的,因此不需要調(diào)用父類的load函數(shù),否則父類的load函數(shù)會(huì)多次執(zhí)行。
1.當(dāng)父類和子類都實(shí)現(xiàn)load函數(shù)時(shí),父類的load方法執(zhí)行順序要優(yōu)先于子類
2.當(dāng)子類未實(shí)現(xiàn)load方法時(shí),不會(huì)調(diào)用父類load方法
3.類中的load方法執(zhí)行順序要優(yōu)先于類別(Category)
4.當(dāng)有多個(gè)類別(Category)都實(shí)現(xiàn)了load方法,這幾個(gè)load方法都會(huì)執(zhí)行,但執(zhí)行順序不確定(其執(zhí)行順序與類別在Compile Sources中出現(xiàn)的順序一致)
5.當(dāng)然當(dāng)有多個(gè)不同的類的時(shí)候,每個(gè)類load 執(zhí)行順序與其在Compile Sources出現(xiàn)的順序一致
下面通過實(shí)例來一起驗(yàn)證下:
我們新建2個(gè)類:Person繼承NSObject,Student和Teacher均繼承Person
Person :NSObjectStudent : PersonTeacher : Person
新建3個(gè)Person分類:
Person (Category)
Person (Category2)
Person (Category3)
在Person,Student,Person (Category),Person (Category2),Person (Category3)的.m文件中實(shí)現(xiàn)下面方法(Teacher除外)
//In Person.m+(void)load{NSLog(@"%s",__FUNCTION__);}//In Student.m(繼承自Person)+(void)load{NSLog(@"%s",__FUNCTION__);}//In Teacher.m(繼承Person)不實(shí)現(xiàn)load方法//In Person+Category.m+(void)load{NSLog(@"%s",__FUNCTION__);}//In Person+Category2.m+(void)load{NSLog(@"%s",__FUNCTION__);}//In Person+Category3.m+(void)load{NSLog(@"%s",__FUNCTION__);}
運(yùn)行結(jié)果:
2017-05-0411:11:40.612LoadAndInitializeExample[27745:856244]+[Person load]2017-05-0411:11:40.615LoadAndInitializeExample[27745:856244]+[Student load]2017-05-0411:11:40.616LoadAndInitializeExample[27745:856244]+[Person(Category3) load]2017-05-0411:11:40.617LoadAndInitializeExample[27745:856244]+[Person(Category) load]2017-05-0411:11:40.617LoadAndInitializeExample[27745:856244]+[Person(Category2) load]
可以看到執(zhí)行順序依次為:
1.首先執(zhí)行的是父類Person load方法,再執(zhí)行的是子類Student load方法,說明父類的load方法執(zhí)行順序要優(yōu)先于子類
2.子類Teacher中沒有實(shí)現(xiàn)load方法,沒有打印,說明子類沒有實(shí)現(xiàn)load方法時(shí)并不會(huì)調(diào)用父類load方法
3.最后執(zhí)行的是Person 3個(gè)Category的load方法,并且沒有順序,說明類別(Category)中的load方法要晚于類,多個(gè)類別(Category)都實(shí)現(xiàn)了load方法,這幾個(gè)load方法都會(huì)執(zhí)行,但執(zhí)行順序不確定
4.同時(shí)我們也可以看到這幾個(gè)Category load 執(zhí)行順序與其在Compile Sources中出現(xiàn)的順序一致
5.當(dāng)然多個(gè)不同的類 其load執(zhí)行順序,也與其在Compile Sources出現(xiàn)的順序一致

Compile Sources.png
initialize:
Apple文檔是這樣描述的
Initializes theclassbefore it receives its first message.在這個(gè)類接收第一條消息之前調(diào)用。DiscussionThe runtime sends initialize to eachclassina program exactly one time just before theclass, or anyclassthat inherits from it, is sent its first message from within the program. (Thus the method may never be invokediftheclassis not used.) The runtime sends the initialize message to classesina thread-safe manner. Superclasses receivethismessage before their subclasses.Runtime在一個(gè)程序中每一個(gè)類的一個(gè)程序中發(fā)送一個(gè)初始化一次,或是從它繼承的任何類中,都是在程序中發(fā)送第一條消息。(因此,當(dāng)該類不使用時(shí),該方法可能永遠(yuǎn)不會(huì)被調(diào)用。)運(yùn)行時(shí)發(fā)送一個(gè)線程安全的方式初始化消息。父類的調(diào)用一定在子類之前。
文檔地址:https://developer.apple.com/reference/objectivec/nsobject/1418639-initialize?language=objc
initialize函數(shù)調(diào)用特點(diǎn)如下:
initialize在類或者其子類的第一個(gè)方法被調(diào)用前調(diào)用。即使類文件被引用進(jìn)項(xiàng)目,但是沒有使用,initialize不會(huì)被調(diào)用。由于是系統(tǒng)自動(dòng)調(diào)用,也不需要再調(diào)用? [super initialize] ,否則父類的initialize會(huì)被多次執(zhí)行。假如這個(gè)類放到代碼中,而這段代碼并沒有被執(zhí)行,這個(gè)函數(shù)是不會(huì)被執(zhí)行的。
1.父類的initialize方法會(huì)比子類先執(zhí)行
2.當(dāng)子類未實(shí)現(xiàn)initialize方法時(shí),會(huì)調(diào)用父類initialize方法,子類實(shí)現(xiàn)initialize方法時(shí),會(huì)覆蓋父類initialize方法.
3.當(dāng)有多個(gè)Category都實(shí)現(xiàn)了initialize方法,會(huì)覆蓋類中的方法,只執(zhí)行一個(gè)(會(huì)執(zhí)行Compile Sources 列表中最后一個(gè)Category 的initialize方法)
我們同樣通過實(shí)例來一起驗(yàn)證下:
我們添加Person類,在.m中實(shí)現(xiàn)+ initialize方法
//In Person.m+(void)initialize{NSLog(@"%s",__FUNCTION__);}
運(yùn)行結(jié)果:
無打印...
啥也沒打印
說明:只是把類文件被引用進(jìn)項(xiàng)目,沒有使用的話,initialize不會(huì)被調(diào)用
我們?cè)赥eacher(繼承Person).m中不實(shí)現(xiàn)initialize方法
//In Person.m+(void)initialize{NSLog(@"%s",__FUNCTION__);}//In Teacher.m(繼承自Person)Teacher.m中不實(shí)現(xiàn)initialize方法
我們調(diào)用下Teacher的new方法,運(yùn)行
[Teachernew];
運(yùn)行結(jié)果:
2017-05-0414:06:45.051LoadAndInitializeExample[29238:912579]+[Person initialize]2017-05-0414:06:45.051LoadAndInitializeExample[29238:912579]+[Person initialize]
運(yùn)行后發(fā)現(xiàn)父類的initialize方法竟然調(diào)用了兩次:
可見當(dāng)子類未實(shí)現(xiàn)initialize方法,會(huì)調(diào)用父類initialize方法.
但為什么會(huì)打印2次呢?
我的理解:
子類不實(shí)現(xiàn)initialize方法,會(huì)把繼承父類的initialize方法并調(diào)用一遍。在此之前,父類初始化時(shí),會(huì)先調(diào)用一遍自己initialize方法.所以出現(xiàn)2遍,所以為了防止父類initialize中代碼多次執(zhí)行,我們應(yīng)該這樣寫:
//In Person.m+(void)initialize{if(self== [Personclass])? ? {NSLog(@"%s",__FUNCTION__);? ? }}
下面我們?cè)?Teacher.m中實(shí)現(xiàn)initialize方法
//In Person.m+(void)initialize{NSLog(@"%s",__FUNCTION__);}//In Teacher.m(繼承自Person)+(void)initialize{NSLog(@"%s",__FUNCTION__);}
同樣調(diào)用Teacher 的new方法,運(yùn)行
[Teachernew];
運(yùn)行結(jié)果:
2017-05-0414:07:45.051LoadAndInitializeExample[29238:912579]+[Person initialize]2017-05-0414:07:45.051LoadAndInitializeExample[29238:912579]+[Teacher initialize]
可以看到 當(dāng)子類實(shí)現(xiàn)initialize方法后,會(huì)覆蓋父類initialize方法,這一點(diǎn)和繼承思想一樣
我們?cè)赑erson.m和Person+Category.m中實(shí)現(xiàn)initialize方法
//In Person.m+(void)initialize{NSLog(@"%s",__FUNCTION__);}//In Person+Category.m+(void)initialize{NSLog(@"%s",__FUNCTION__);}
調(diào)用Person 的 new方法,運(yùn)行
[Personnew];
運(yùn)行結(jié)果:
2017-05-0509:46:51.054LoadAndInitializeExample[38773:1226306]+[Person(Category) initialize]
運(yùn)行后可以看到Person的initialize方法并沒有被執(zhí)行,已經(jīng)被Person+Category中的initialize取代了
當(dāng)有多個(gè)Category時(shí)會(huì)怎樣了,我們?cè)赑erson,Person+Category,Person+Category2,Person+Category3 的.m中都實(shí)現(xiàn)initialize方法
//In Person.m+(void)initialize{NSLog(@"%s",__FUNCTION__);}//In Person+Category.m+(void)initialize{NSLog(@"%s",__FUNCTION__);}//In Person+Category2.m+(void)initialize{NSLog(@"%s",__FUNCTION__);}//In Person+Category3.m+(void)initialize{NSLog(@"%s",__FUNCTION__);}
調(diào)用Person new方法,運(yùn)行
[Personnew];
運(yùn)行結(jié)果:
2017-05-0509:49:38.853LoadAndInitializeExample[38825:1227819]+[Person(Category2) initialize]
可以看到,當(dāng)存在多個(gè)Category時(shí),也只執(zhí)行一個(gè),具體執(zhí)行哪一個(gè)Category中的initialize方法,測(cè)試后便可發(fā)現(xiàn),會(huì)執(zhí)行Compile Sources 列表中最后一個(gè)Category 的initialize方法

Compile Sources.png
什么情況下使用:
+load
由于調(diào)用load方法時(shí)的環(huán)境很不安全,我們應(yīng)該盡量減少load方法的邏輯。另一個(gè)原因是load方法是線程安全的,它內(nèi)部使用了鎖,所以我們應(yīng)該避免線程阻塞在load方法中
load很常見的一個(gè)使用場(chǎng)景,交換兩個(gè)方法的實(shí)現(xiàn)
//摘自MJRefresh+ (void)load{? ? [selfexchangeInstanceMethod1:@selector(reloadData) method2:@selector(mj_reloadData)];? ? [selfexchangeInstanceMethod1:@selector(reloadRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_reloadRowsAtIndexPaths:withRowAnimation:)];? ? [selfexchangeInstanceMethod1:@selector(deleteRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_deleteRowsAtIndexPaths:withRowAnimation:)];? ? [selfexchangeInstanceMethod1:@selector(insertRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_insertRowsAtIndexPaths:withRowAnimation:)];? ? [selfexchangeInstanceMethod1:@selector(reloadSections:withRowAnimation:) method2:@selector(mj_reloadSections:withRowAnimation:)];? ? [selfexchangeInstanceMethod1:@selector(deleteSections:withRowAnimation:) method2:@selector(mj_deleteSections:withRowAnimation:)];? ? [selfexchangeInstanceMethod1:@selector(insertSections:withRowAnimation:) method2:@selector(mj_insertSections:withRowAnimation:)];}+ (void)exchangeInstanceMethod1:(SEL)method1 method2:(SEL)method2{? ? method_exchangeImplementations(class_getInstanceMethod(self, method1), class_getInstanceMethod(self, method2));}
+initialize
initialize方法主要用來對(duì)一些不方便在編譯期初始化的對(duì)象進(jìn)行賦值。比如NSMutableArray這種類型的實(shí)例化依賴于runtime的消息發(fā)送,所以顯然無法在編譯器初始化:
// In Person.m// int類型可以在編譯期賦值staticintsomeNumber =0;staticNSMutableArray*someArray;+ (void)initialize {if(self== [Personclass]) {// 不方便編譯期復(fù)制的對(duì)象在這里賦值someArray = [[NSMutableArrayalloc] init];? ? }}
總結(jié):
load和initialize的共同點(diǎn)
1.如果父類和子類都被調(diào)用,父類的調(diào)用一定在子類之前
+load方法要點(diǎn)
當(dāng)類被引用進(jìn)項(xiàng)目的時(shí)候就會(huì)執(zhí)行l(wèi)oad函數(shù)(在main函數(shù)開始執(zhí)行之前),與這個(gè)類是否被用到無關(guān),每個(gè)類的load函數(shù)只會(huì)自動(dòng)調(diào)用一次.由于load函數(shù)是系統(tǒng)自動(dòng)加載的,因此不需要再調(diào)用[super load],否則父類的load函數(shù)會(huì)多次執(zhí)行。
1.當(dāng)父類和子類都實(shí)現(xiàn)load函數(shù)時(shí),父類的load方法執(zhí)行順序要優(yōu)先于子類
2.當(dāng)一個(gè)類未實(shí)現(xiàn)load方法時(shí),不會(huì)調(diào)用父類load方法
3.類中的load方法執(zhí)行順序要優(yōu)先于類別(Category)
4.當(dāng)有多個(gè)類別(Category)都實(shí)現(xiàn)了load方法,這幾個(gè)load方法都會(huì)執(zhí)行,但執(zhí)行順序不確定(其執(zhí)行順序與類別在Compile Sources中出現(xiàn)的順序一致)
5.當(dāng)然當(dāng)有多個(gè)不同的類的時(shí)候,每個(gè)類load 執(zhí)行順序與其在Compile Sources出現(xiàn)的順序一致
注意:
load調(diào)用時(shí)機(jī)比較早,當(dāng)load調(diào)用時(shí),其他類可能還沒加載完成,運(yùn)行環(huán)境不安全.
load方法是線程安全的,它使用了鎖,我們應(yīng)該避免線程阻塞在load方法.
+initialize方法要點(diǎn)
initialize在類或者其子類的第一個(gè)方法被調(diào)用前調(diào)用。即使類文件被引用進(jìn)項(xiàng)目,但是沒有使用,initialize不會(huì)被調(diào)用。由于是系統(tǒng)自動(dòng)調(diào)用,也不需要顯式的調(diào)用父類的initialize,否則父類的initialize會(huì)被多次執(zhí)行。假如這個(gè)類放到代碼中,而這段代碼并沒有被執(zhí)行,這個(gè)函數(shù)是不會(huì)被執(zhí)行的。
1.父類的initialize方法會(huì)比子類先執(zhí)行
2.當(dāng)子類不實(shí)現(xiàn)initialize方法,會(huì)把父類的實(shí)現(xiàn)繼承過來調(diào)用一遍。在此之前,父類的方法會(huì)被優(yōu)先調(diào)用一次
3.當(dāng)有多個(gè)Category都實(shí)現(xiàn)了initialize方法,會(huì)覆蓋類中的方法,只執(zhí)行一個(gè)(會(huì)執(zhí)行Compile Sources 列表中最后一個(gè)Category 的initialize方法)
注意:
在initialize方法收到調(diào)用時(shí),運(yùn)行環(huán)境基本健全。
initialize內(nèi)部也使用了鎖,所以是線程安全的。但同時(shí)要避免阻塞線程,不要再使用鎖
轉(zhuǎn)載至:http://m.itdecent.cn/p/c52d0b6ee5e9