1. load
結(jié)論:
- +(void)load方法無(wú)需手動(dòng)調(diào)用槽袄,當(dāng)類被runtime加載后盆赤,自動(dòng)調(diào)用(如果實(shí)現(xiàn)的話)
- 調(diào)用順序按照 [super class] -> [class] -> [sub class] -> [Category]
- 若有多個(gè)[child class]虚缎,則按照compile source順序蚣旱,但要遵循先調(diào)用[super class]再調(diào)用[child class]钦购;
若有多個(gè)類別翻翩,則優(yōu)先全部類都许,然后再類別,類別的順序完全按照compile source順序嫂冻。 - 不遵循繼承覆蓋那套胶征,基于2、3調(diào)用順序桨仿,有就調(diào)用睛低。若子類未寫load,則不會(huì)調(diào)用父類的load服傍。
- 正常情況下钱雷,load只會(huì)被runtime加載后調(diào)用一次。但是如果吹零,我們?nèi)藶閷懥薣super load]罩抗,父類的load方法會(huì)調(diào)用兩次。
1.1 +(void)load方法無(wú)需手動(dòng)調(diào)用瘪校,當(dāng)類被runtime加載后澄暮,自動(dòng)調(diào)用(如果實(shí)現(xiàn)的話)
新建 Person名段、Student兩個(gè)類,Student繼承Person泣懊;同時(shí)再新建Student+Category分類伸辟;
// Person類,實(shí)現(xiàn)load方法
@implementation Person
+(void)load {
NSLog(@"%s", __FUNCTION__);
}
@end
//Student類馍刮,實(shí)現(xiàn)load方法
@implementation Student
+(void)load {
NSLog(@"%s", __FUNCTION__);
}
@end
//Student+Category類別信夫,實(shí)現(xiàn)load方法
@implementation Student (StudentCategory)
+(void)load {
NSLog(@"%s", __FUNCTION__);
}
@end
同時(shí),我們?cè)趍ain寫上打印
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
NSLog(@"main ...");
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
此時(shí)執(zhí)行卡啰,輸出:
2021-03-15 15:35:31.085844+0800 TestDemo[6593:77525] +[Person load]
2021-03-15 15:35:31.086350+0800 TestDemo[6593:77525] +[Student load]
2021-03-15 15:35:31.086437+0800 TestDemo[6593:77525] +[Student(StudentCategory) load]
2021-03-15 15:35:31.086577+0800 TestDemo[6593:77525] main ...
可以看出:
- 我們未做任何調(diào)用,自動(dòng)調(diào)用了實(shí)現(xiàn)的load方法静稻。
- main...打印在所有的load方法之后
Invoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.
Declaration
- (void)load;
//蘋果官方文檔這樣描述:
當(dāng)一個(gè)類或分類被加載到runtime時(shí)被調(diào)用;實(shí)現(xiàn)此方法在加載時(shí)執(zhí)行類的特定行為匈辱。
1.2 調(diào)用順序按照 [super class] -> [class] -> [sub class] -> [Category]
基于1.1的打印振湾,其實(shí)我們已經(jīng)看出來,先調(diào)用了[person load] -> [Student load] -> [Student(StudentCategory) load]
此時(shí)亡脸,我們添加多個(gè)子類押搪,看看效果如何。
//新建Teacher浅碾,繼承自Person
@implementation Teacher
+(void)load {
NSLog(@"%s", __FUNCTION__);
}
@end
此時(shí)結(jié)果為:
2021-03-15 16:10:59.705768+0800 TestDemo[9791:115517] +[Person load]
2021-03-15 16:10:59.706308+0800 TestDemo[9791:115517] +[Teacher load]
2021-03-15 16:10:59.706414+0800 TestDemo[9791:115517] +[Student load]
2021-03-15 16:10:59.706484+0800 TestDemo[9791:115517] +[Student(StudentCategory) load]
2021-03-15 16:10:59.706602+0800 TestDemo[9791:115517] main ...
那為何Teacher先于Student調(diào)用load大州,這其中的順序?yàn)楹危?/p>
1.3 調(diào)用順序
1.3.1 若有多個(gè)[child class],則按照compile source順序垂谢,但要遵循先調(diào)用[super class]再調(diào)用[child class]
我們看下compile source順序
此時(shí)Teacher.m位于第一位厦画,那么我們調(diào)整下編譯順序,看下
此時(shí)輸出: (可以看到滥朱,編譯順序的確影響了load的調(diào)用順序根暑。但是Person無(wú)論怎樣,都先于child調(diào)用了徙邻。并且子類無(wú)法覆蓋父類的load方法购裙。)
2021-03-15 16:15:16.092491+0800 TestDemo[10185:120946] +[Person load]
2021-03-15 16:15:16.092981+0800 TestDemo[10185:120946] +[Student load]
2021-03-15 16:15:16.093099+0800 TestDemo[10185:120946] +[Teacher load]
2021-03-15 16:15:16.093292+0800 TestDemo[10185:120946] +[Student(StudentCategory) load]
2021-03-15 16:15:16.093470+0800 TestDemo[10185:120946] main ...
我們?cè)偌酉麓a,新建一個(gè)StudentBoy鹃栽,繼承自Student。所以此時(shí)studentBoy的繼承關(guān)系為 Person->Student->StudentBoy
此時(shí)的結(jié)果為:
2021-03-15 16:26:02.355757+0800 TestDemo[11200:136372] +[Person load]
2021-03-15 16:26:02.356303+0800 TestDemo[11200:136372] +[Student load]
2021-03-15 16:26:02.356396+0800 TestDemo[11200:136372] +[StudentBoy load]
2021-03-15 16:26:02.356494+0800 TestDemo[11200:136372] +[Teacher load]
2021-03-15 16:26:02.356581+0800 TestDemo[11200:136372] +[Student(StudentCategory) load]
2021-03-15 16:26:02.356762+0800 TestDemo[11200:136372] main ...
解釋:
StudentBoy.m為第一個(gè)compile source文件躯畴。StudentBoy父類為Student民鼓,Student的父類又為Person。
所以依此打印了Person load -> Student load -> Person load蓬抄。
即使Teacher.m和Student.m平級(jí)丰嘉,且在Student.m之前,也不會(huì)改變這個(gè)順序嚷缭。
1.3.2 若有多個(gè)類別饮亏,則優(yōu)先全部類耍贾,然后再類別,類別的順序完全按照compile source順序路幸。
我們新增一個(gè)Teacher+Category類別
@implementation Teacher (TeacherCategory)
+(void)load {
NSLog(@"%s", __FUNCTION__);
}
@end
運(yùn)行下
2021-03-15 17:51:11.270081+0800 TestDemo[22538:256893] +[Person load]_block_invoke
2021-03-15 17:51:11.270590+0800 TestDemo[22538:256893] +[Student load]
2021-03-15 17:51:11.270729+0800 TestDemo[22538:256893] +[Person initialize]
2021-03-15 17:51:11.270879+0800 TestDemo[22538:256893] +[Person initialize]
2021-03-15 17:51:11.271064+0800 TestDemo[22538:256893] +[StudentBoy load]
2021-03-15 17:51:11.271144+0800 TestDemo[22538:256893] +[Teacher load]
2021-03-15 17:51:11.271223+0800 TestDemo[22538:256893] +[Teacher(TeacherCategory) load]
2021-03-15 17:51:11.271307+0800 TestDemo[22538:256893] +[Student(StudentCategory) load]
2021-03-15 17:51:11.271455+0800 TestDemo[22538:256893] main ...
可以看到荐开,所有的類load執(zhí)行完了,才執(zhí)行了category简肴』翁可以多加幾個(gè)category進(jìn)行驗(yàn)證。
那同是category砰识,順序又如何呢能扒?
上面的代碼Compile source順序?yàn)椋?/p>
可以看到Teacher+Category先于Student+Category,那么我們調(diào)整下compile source順序看看辫狼。
結(jié)果為:
2021-03-15 17:56:28.536222+0800 TestDemo[23323:265134] +[Person load]_block_invoke
2021-03-15 17:56:28.536641+0800 TestDemo[23323:265134] +[Student load]
2021-03-15 17:56:28.536759+0800 TestDemo[23323:265134] +[Person initialize]
2021-03-15 17:56:28.536831+0800 TestDemo[23323:265134] +[Person initialize]
2021-03-15 17:56:28.537031+0800 TestDemo[23323:265134] +[StudentBoy load]
2021-03-15 17:56:28.537108+0800 TestDemo[23323:265134] +[Teacher load]
2021-03-15 17:56:28.537197+0800 TestDemo[23323:265134] +[Student(StudentCategory) load]
2021-03-15 17:56:28.537268+0800 TestDemo[23323:265134] +[Teacher(TeacherCategory) load]
2021-03-15 17:56:28.537380+0800 TestDemo[23323:265134] main ...
可以得到一些結(jié)論:
- 無(wú)論category順序如何調(diào)整初斑,總是先執(zhí)行了class的load,然后才是分類的膨处。
- 在同是分類的級(jí)別里见秤,Compile source在前的先執(zhí)行。
1.3.3 若子類不寫load灵迫,是否會(huì)調(diào)用父類的load秦叛?
我們屏蔽Student.m內(nèi)的+(void)load方法。
此時(shí)結(jié)果為:
2021-03-15 18:18:34.252822+0800 TestDemo[26572:294490] +[Person load]
2021-03-15 18:18:34.253213+0800 TestDemo[26572:294490] +[Student(StudentCategory) load]
2021-03-15 18:18:34.253328+0800 TestDemo[26572:294490] +[StudentBoy load]
2021-03-15 18:18:34.253438+0800 TestDemo[26572:294490] +[Teacher load]
2021-03-15 18:18:34.253516+0800 TestDemo[26572:294490] +[Teacher(TeacherCategory) load]
2021-03-15 18:18:34.253649+0800 TestDemo[26572:294490] main ...
可以看到并未調(diào)用[Student load],但是有人會(huì)問瀑粥,那不是還有 [Person load]打印了嗎挣跋?
其實(shí)那不是Student調(diào)用的打印,如果Student會(huì)調(diào)用父類的狞换,那 [Person load]應(yīng)該會(huì)被打印兩次才對(duì)避咆。在后面的initilalize你會(huì)看到明顯的區(qū)別。
1.4 正常情況下修噪,load只會(huì)被runtime加載后調(diào)用一次查库。但是如果,我們?nèi)藶閷懥薣super load]黄琼,父類的load方法會(huì)調(diào)用兩次樊销。
我們?cè)赟tudent的load方法內(nèi)寫入[super load],看看會(huì)發(fā)生什么
@implementation Student
+(void)load {
[super load]; //此處增加super load
NSLog(@"%s", __FUNCTION__);
}
@end
結(jié)果
2021-03-15 16:30:26.190810+0800 TestDemo[11595:140810] +[Person load]
2021-03-15 16:30:26.191265+0800 TestDemo[11595:140810] +[Person load]
2021-03-15 16:30:26.191399+0800 TestDemo[11595:140810] +[Student load]
2021-03-15 16:30:26.191509+0800 TestDemo[11595:140810] +[StudentBoy load]
2021-03-15 16:30:26.191600+0800 TestDemo[11595:140810] +[Teacher load]
2021-03-15 16:30:26.191676+0800 TestDemo[11595:140810] +[Student(StudentCategory) load]
2021-03-15 16:30:26.191812+0800 TestDemo[11595:140810] main ...
我們發(fā)現(xiàn)脏款,[Person load]也會(huì)被調(diào)用兩次围苫。
那么,我們?nèi)绾我?guī)避load有可能被調(diào)用兩次撤师?
@implementation Person
+(void)load {
//使用GCD的dispatch_once剂府,讓包裹內(nèi)容只執(zhí)行一次。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"%s", __FUNCTION__);
});
}
@end
load用處
由于調(diào)用load方法時(shí)的環(huán)境很不安全剃盾,我們應(yīng)該盡量減少load方法的邏輯腺占。另一個(gè)原因是load方法是線程安全的淤袜,它內(nèi)部使用了鎖,所以我們應(yīng)該避免線程阻塞在load方法中衰伯。
load常見于交換方法
@implementation Student
+(void)load {
NSLog(@"%s", __FUNCTION__);
static dispatch_once_t oneToken;
dispatch_once(&oneToken, ^{
[Student swizzleMethod:NSSelectorFromString(@"dealloc") byNewMethod:@selector(swizzleDealloc)];
});
}
//交換方法
+ (void)swizzleMethod:(SEL)originalSelector byNewMethod:(SEL)newSelector {
Class class = [self class];
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method newMethod = class_getInstanceMethod(class, newSelector);
bool didAdd = class_addMethod(class,
originalSelector,
method_getImplementation(newMethod),
method_getTypeEncoding(newMethod));
if (didAdd) {
class_replaceMethod(class,
newSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, newMethod);
}
}
- (void)dealloc{
NSLog(@"%s", __FUNCTION__);
}
- (void)swizzleDealloc{
NSLog(@"%s", __FUNCTION__);
[self swizzleDealloc];
}
@end
結(jié)果輸出:
2021-03-15 17:40:37.872298+0800 TestDemo[20887:239992] +[Person load]_block_invoke
2021-03-15 17:40:37.872658+0800 TestDemo[20887:239992] +[Student load]
2021-03-15 17:40:37.872849+0800 TestDemo[20887:239992] +[StudentBoy load]
2021-03-15 17:40:37.872914+0800 TestDemo[20887:239992] +[Teacher load]
2021-03-15 17:40:37.872981+0800 TestDemo[20887:239992] +[Student(StudentCategory) load]
2021-03-15 17:40:37.873096+0800 TestDemo[20887:239992] main ...
2021-03-15 17:40:37.913371+0800 TestDemo[20887:239992] student >>>>>>
2021-03-15 17:40:37.913491+0800 TestDemo[20887:239992] -[Student swizzleDealloc]
2021-03-15 17:40:37.913596+0800 TestDemo[20887:239992] -[Student dealloc]
2021-03-15 17:40:37.913683+0800 TestDemo[20887:239992] student <<<<<<
可見铡羡,我們成功的使用了自定義的dealloc方法。
2. initialize
我們先看apple文檔解釋
Summary
Initializes the class before it receives its first message.
在類接受到第一個(gè)消息之前 (何為第一個(gè)消息嚎研?我們直到蓖墅,runtime內(nèi),調(diào)用方法其實(shí)就是發(fā)送消息临扮。所以可以認(rèn)為調(diào)用第一個(gè)方法之前论矾。)
結(jié)論:
- initialize在接收到第一個(gè)消息之前被觸發(fā)(initialize屬于懶加載,所以只要不調(diào)用方法杆勇,是不會(huì)觸發(fā)initialize)贪壳,無(wú)需顯示調(diào)用,自動(dòng)調(diào)用蚜退。
- 若子類未實(shí)現(xiàn)initialize闰靴,會(huì)默認(rèn)繼承父類的并執(zhí)行一遍。
- Category會(huì)覆蓋類中的initialize方法钻注,當(dāng)有多個(gè)Category蚂且,會(huì)執(zhí)行Compile source最后一個(gè)category的initialize
2.1 initialize在接收到第一個(gè)消息之前被觸發(fā),無(wú)需顯示調(diào)用幅恋,自動(dòng)調(diào)用杏死。
基于以上代碼,分別有 Person.m捆交、Student.m(繼承Person)淑翼、Student+Category.m(Student分類)、StudentBoy.m(繼承Student) 以上都實(shí)現(xiàn)了 + (void)initialize
@implementation Student
+ (void)initialize {
NSLog(@"%s", __FUNCTION__);
}
- (id)init {
self = [super init];
if (self) {
// Initialize self.
NSLog(@"init 方法被調(diào)用");
}
return self;
}
@end
//ViewController.m文件
//在viewController初始化一個(gè)局部變量
NSLog(@"StudentBoy >>>>>>");
[[StudentBoy alloc] init];
NSLog(@"StudentBoy <<<<<<");
結(jié)果
2021-03-15 18:39:39.684465+0800 TestDemo[29769:329186] +[StudentBoy load]
2021-03-15 18:39:39.684832+0800 TestDemo[29769:329186] +[Teacher load]
2021-03-15 18:39:39.684925+0800 TestDemo[29769:329186] +[Teacher(TeacherCategory) load]
2021-03-15 18:39:39.685040+0800 TestDemo[29769:329186] main ...
2021-03-15 18:39:39.734637+0800 TestDemo[29769:329186] StudentBoy >>>>>>
2021-03-15 18:39:39.734726+0800 TestDemo[29769:329186] +[Person initialize]
2021-03-15 18:39:39.734811+0800 TestDemo[29769:329186] +[Student(StudentCategory) initialize]
2021-03-15 18:39:39.734894+0800 TestDemo[29769:329186] +[StudentBoy initialize]
2021-03-15 18:39:39.734987+0800 TestDemo[29769:329186] init 方法被調(diào)用
2021-03-15 18:39:39.735073+0800 TestDemo[29769:329186] StudentBoy <<<<<<
- 可以回顧之前的load品追,先于main玄括。 而initialize在調(diào)用init之前被調(diào)用。
- Student的initialize被+[Student(StudentCategory) initialize]覆蓋
- 從父類->子類的順序調(diào)用
2.2若子類不實(shí)現(xiàn)initialize不實(shí)現(xiàn)肉瓦,會(huì)繼承父類的遭京,且執(zhí)行一次。(所以經(jīng)常在inialize內(nèi)部要判斷是哪個(gè)類泞莉,有可能是子類在調(diào)用)
我們刪除 Student.m 以及 Student+Category.m內(nèi)的 initialize洁墙。
并修改Person.m內(nèi)的initialize,打印出當(dāng)前類:
@implementation Person
+ (void)initialize {
NSLog(@"%s class=%@", __FUNCTION__, self);
}
@end
執(zhí)行結(jié)果:
2021-03-15 18:45:43.107517+0800 TestDemo[30691:338134] +[StudentBoy load]
2021-03-15 18:45:43.108253+0800 TestDemo[30691:338134] +[Teacher load]
2021-03-15 18:45:43.108354+0800 TestDemo[30691:338134] +[Teacher(TeacherCategory) load]
2021-03-15 18:45:43.108562+0800 TestDemo[30691:338134] main ...
2021-03-15 18:45:43.151152+0800 TestDemo[30691:338134] StudentBoy >>>>>>
2021-03-15 18:45:43.151278+0800 TestDemo[30691:338134] +[Person initialize] class=Person
2021-03-15 18:45:43.151398+0800 TestDemo[30691:338134] +[Person initialize] class=Student
2021-03-15 18:45:43.151522+0800 TestDemo[30691:338134] +[StudentBoy initialize]
2021-03-15 18:45:43.151647+0800 TestDemo[30691:338134] init 方法被調(diào)用
2021-03-15 18:45:43.151760+0800 TestDemo[30691:338134] StudentBoy <<<<<<
可以看到 +[Person initialize]被調(diào)用了兩次戒财,然后但是class卻不同。
initialize用途
一般initialize用來初始化靜態(tài)變量和全局變量等捺弦,我們也可以利用initialize的線程安全饮寞,來實(shí)現(xiàn)單例孝扛,例如:
static Printer *instance = nil;
@implementation Printer
+ (void)initialize {
if (!instance) {
instance = [[Printer alloc] init];
}
}
+ (Printer *)instance {
return instance;
}
//other methods
@end