+load和+initialize方法都是NSObject的兩個類方法,iOS會在運行期提前調用這兩個方法,那么我們可以在這兩個方法中做一些處理。
1、runtime并不將類的+load方法作為類的第一個方法執行
.h文件
@interface SuperClass : NSObject
@end
@interface ChildClass : SuperClass
@end
@interface Insideinitialize : NSObject
- (void)objectMethod;
@end
.m文件
/******* Implementation *******/
@implementation SuperClass
+ (void) initialize {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
+ (void) load {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
@end
@implementation ChildClass
+ (void) initialize {
NSLog(@"%@ %s", [self class], __FUNCTION__);
Insideinitialize * obj = [[Insideinitialize alloc] init];
[obj objectMethod];
}
@end
@implementation Insideinitialize
- (void)objectMethod {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
+ (void) initialize {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
+ (void) load {
NSLog(@"%s", __FUNCTION__);
}
@end
在上面的情況下,只是引用了,打印出來
說明
就像Apple的文檔中說的一下,只要有引用runtime就會自動去調用類的+(void)load方法。不過從輸出中,我們還發現SuperClass的+(void)initialize也被調用了,而且是在+(void)load之前被執行;而Insideinitialize的+(void)initialize并沒有執行。這是因為在SuperClass的+(void)load方法中,我們調用了類的class方法([self class]),這就符合文檔中對+(void)initialize的說明:在類的第一個方法被調用前調用。同時也說明runtime對+(void)load的調用并不視為類的第一個方法。而ChildClass因為沒有用到,所以+(void)initialize的方法被沒有被執行,而且它也沒有去執行父類的+(void)load方法(雖然它有繼承下該方法)。
當我們將ChildClass的+(void)load方法重寫,發現它有調用這個+(void)load方法
.m文件中ChildClass的實現
@implementation ChildClass
+ (void) initialize {
NSLog(@"%@ %s", [self class], __FUNCTION__);
Insideinitialize * obj = [[Insideinitialize alloc] init];
[obj objectMethod];
}
+ (void) load {
NSLog(@"ChildClass %s", __FUNCTION__);
}
@end
打印結果:
2、+(void)load方法可以當做普通的類方法來調用
在程序中手動調用+load方法,同時將ChildClass的+load方法注釋掉
- (void)viewDidLoad {
[super viewDidLoad];
[ChildClass load];
}
/**
+ (void) load {
NSLog(@"ChildClass %s", __FUNCTION__);
}
*/
打印結果:
說明
前面三個結果很容易理解,不過之后ChildClass的+(void)initialize也被自動執行調用,這個是為什么呢?因為我們是直接調用了ChildClass的類方法(+(void)load方法),所以這里會首先的調用ChildClass的+(void)initialize方法,并且我們可以在其中安全創建出Insideinitialize類并使用它,而Insideinitialize因為調用alloc方法是第一次使用類方法, 所以激發了Insideinitialize的+(void)initialize。
另一個方面,ChildClass繼承下了+(void)load而且可以被安全地當做普通類方法(Class Method)被使用。這也就是我之前所說的load和initialize被調用一次是相對runtime而言。
同時,最后一個ChildClass調用+(void)load方法,其實是去調用父類的+(void)load方法
(比如SuperClass的initialize不會因為自身load方法調用一次,又因為子類調用了load又執行一次),我們依然可以直接去反復調用這些方法。
3、子類的+(void)initialize會調用父類的+(void)initialize方法
修改代碼如下,去掉SuperClass中的+(void)load方法;讓ChildClass來重寫+(void)load,同時也去掉+(void)initialize。
在程序中不手動調用方法。
/******* Implementation *******/
@implementation SuperClass
+ (void) initialize {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
/**
+ (void) load {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
*/
@end
@implementation ChildClass
/**
+ (void) initialize {
NSLog(@"%@ %s", [self class], __FUNCTION__);
Insideinitialize * obj = [[Insideinitialize alloc] init];
[obj objectMethod];
}
*/
+ (void) load {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
// [ChildClass load];
}
打印結果:
說明
和之前一樣,+(void)load會引起+(void)initialize。也很Apple文檔中講得那樣,子類方法的調用會激起父類的+(void)initialize被執行。不過我們也看到雖然ChildClass沒有定義+(void)initialize,但是它會使用父類的+(void)initialize。而之前的示例,我們看到子類并不會在runtime時去使用父類的+(void)load,也就是說只有新定義的+(void)load才會被runtime去調用執行。
4、類別中的+(void)load方法和+(void)initialize方法
新建一個類,同時增加兩個類別
.h
@interface MainClass : NSObject
@end
.m
/******* Category Implementation *******/
@implementation MainClass(Category)
+ (void) load {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
+ (void) initialize {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
@end
@implementation MainClass(OtherCategory)
+ (void) load {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
+ (void) initialize {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
@end
/******* Implementation *******/
@implementation MainClass
+ (void) load {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
+ (void) initialize {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
@end
打印結果:
說明
同樣的+(void)initialize優先于+(void)load先執行。但是很明顯的不同在于,只有最后一個類別(Category)的+(void)initialize執行,其他兩個都被隱藏,這就是平常說的類別中的方法會“覆蓋”類中的方法。而對于+(void)load,三個都執行,并且如果Apple的文檔中介紹順序一樣:先執行類自身的實現,再執行類別(Category)中的實現。
5、不需要顯示使用super調用父類中的方法
/******* Category Implementation *******/
@implementation MainClass(Category)
+ (void) load {
[super load];
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
+ (void) initialize {
[super initialize];
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
@end
在調用+(void)load方法和+(void)initialize方法的時候,我們并不需要手動的寫入super的調用,默認他會實現父類的調用,比如上個小節中ChildClass的+(void)initialize方法自動調用了SuperClass的+(void)initialize方法
總結
- +(void)load是只要類所在文件被引用就會被調用,是在main函數之前,而+(void)initialize是在類或者其子類的第一個方法被調用前調用。所以如果類沒有被引用進項目,就不會有load調用;但即使類文件被引用進來,但是沒有使用,那么initialize 也不會被調用。+initialize的調用發生在+init方法之前。
- +(void)initialize只會被調用一次,Category中的+initialize方法會覆蓋類本身的+initialize方法,而在Category中+load方法卻不會覆蓋本身類中的+load 方法。如果父類中實現了+initialize方法,而子類中沒有重寫此方法,則子類初始化時就會使用父類的方法([super initialize])。
- runtime并不將類的+load方法作為類的第一個方法執行,如果在+load方法里調用了類的方法([self class]),那么此時就會先調用類的+initialize方法。
- 如果子類沒有重寫+initialize方法,那么子類會自動的調用父類的+initialize方法,而子類沒有重寫+load方法,雖然子類繼承了父類的+load方法,但是系統并不會主動去調用父類+load方法,只有當外界手動調用的時候才回去調用父類的方法。
- +(void)load方法在在類別中和類本身中都會調用,調用順序是先類,然后是類別,而+(void)initialize方法是只會調用類別中方法。
- +(void)load調用時機比較早,運行環境有不確定因素。在App啟動時進行加載,進行load調用的時候,并不能保證所有類都加載完成且可用,而且這個時候的autorelease pool還沒有創建出來,所以那些依賴autorelease pool的代碼都會有問題,這個時候就要自己負責做auto release處理,不要使用類方法創建對象,要用alloc創建對象然后做處理。