每日一問07——+load&+initialize

簡介:+ initialize 和 + load 是 NSObject 類的兩個類方法啤覆,它們會在運(yùn)行時自動調(diào)用。

先看個例子

@interface BaseClass : NSObject
@end

@implementation BaseClass
+ (void)initialize {
    NSLog(@"%@ , %s", [self class], __FUNCTION__);
}

@end
@interface SubClass : BaseClass
@end

@implementation SubClass
+ (void)load {
    NSLog(@"SubClass load");
}

@end

運(yùn)行程序后宅楞,打印結(jié)果為SubClass load针姿。說明程序啟動會自動調(diào)用load,而不會調(diào)用+ (void)initialize

再改進(jìn)一下例子

int main(int argc, char * argv[]) {
    @autoreleasepool {
        BaseClass *class = [[BaseClass alloc] init];
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

在main函數(shù)里面添加BaseClass初始化操作厌衙。打印結(jié)果為SubClass load ,+[BaseClass initialize] 距淫。先猜測一下,+load是會100%調(diào)用的婶希,而+ initialize只會在這個類被使用的時候才被調(diào)用榕暇。

+ initialize

先看看 NSObject Class Reference 中關(guān)于 +initialize
的說明:

1.+ (void)initialize 消息是在該類接收到其第一個消息之前調(diào)用。關(guān)于這里的第一個消息需要特別說明一下喻杈,對于 NSObjectruntime 機(jī)制而言拐揭,其在調(diào)用 NSObject+ (void)load 消息不被視為第一個消息,但是奕塑,如果像普通函數(shù)調(diào)用一樣直接調(diào)用 NSObject+ (void)load 消息堂污,則會引起 + (void)initialize 的調(diào)用。反之龄砰,如果沒有向 NSObject 發(fā)送第一個消息盟猖,+ (void)initialize 則不會被自動調(diào)用。

2.在應(yīng)用程序的生命周期中换棚,runtime 只會向每個類發(fā)送一次 + (void)initialize 消息式镐,如果該類是子類,且該子類中沒有實(shí)現(xiàn) + (void)initialize 消息固蚤,或者子類顯示調(diào)用父類實(shí)現(xiàn) [super initialize], 那么則會調(diào)用其父類的實(shí)現(xiàn)娘汞。也就是說,父類的 + (void)initialize 可能會被調(diào)用多次夕玩。

3.如果類包含分類你弦,且分類重寫了initialize方法惊豺,那么則會調(diào)用分類的initialize 實(shí)現(xiàn),而原類的該方法實(shí)現(xiàn)不會被調(diào)用禽作,這個機(jī)制同 NSObject 的其他方法(除 + (void)load 方法) 一樣尸昧,即如果原類同該類的分類包含有相同的方法實(shí)現(xiàn),那么原類的該方法被隱藏而無法被調(diào)用旷偿。

4.父類的 initialize 方法先于子類的 initialize 方法調(diào)用烹俗。

  • 先改進(jìn)一下例子
@implementation BaseClass

+ (void)initialize {
//    NSLog(@"%@ , %s", [self class], __FUNCTION__);
    NSLog(@"%s", __FUNCTION__);
}

@end

@implementation SubClass

+ (void)initialize {
    NSLog(@"%s", __FUNCTION__);
}

@end

int main(int argc, char * argv[]) {
    @autoreleasepool {
        SubClass *class = [[SubClass alloc] init];
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

打印結(jié)果為+[BaseClass initialize],+[SubClass initialize]。驗(yàn)證結(jié)果先調(diào)用了BaseClassinitialize后調(diào)用了SubClassinitialize

  • ViewController里測試
@implementation ViewController

- (void)viewWillAppear:(BOOL)animated {
    SubClass *class = [[SubClass alloc] init];
    NSLog(@"viewWillAppear");
}

- (void)viewDidLoad {
    [super viewDidLoad];
    SubClass *class = [[SubClass alloc] init];
    NSLog(@"viewDidLoad");
}
@end

打印結(jié)果為 +[BaseClass initialize],+[SubClass initialize],viewDidLoad,viewWillAppear萍程。說明runtime的確只會向每個類發(fā)送一次+ (void)initialize 消息幢妄。

  • 給SubClass添加一個分類試試
@implementation SubClass (test)

+ (void)initialize {
    NSLog(@"%s", __FUNCTION__);
}

@end
@implementation ViewController

- (void)viewWillAppear:(BOOL)animated {
    SubClass *class = [[SubClass alloc] init];
    NSLog(@"viewWillAppear");
}

- (void)viewDidLoad {
    [super viewDidLoad];
    SubClass *class = [[SubClass alloc] init];
    NSLog(@"viewDidLoad");
}
@end

打印結(jié)果是+[BaseClass initialize],+[SubClass(test) initialize]茫负,可以看出當(dāng)分類實(shí)現(xiàn)+initialize后會覆蓋掉原來類中的+initialize方法

+load

先看看 NSObject Class Reference 中關(guān)于 + (void)load
的說明:

1.+ (void)load 會在類或者類的分類添加到 Objective-c runtime 時調(diào)用磁浇,該調(diào)用發(fā)生在 application:willFinishLaunchingWithOptions: 調(diào)用之前調(diào)用。
2.父類的 +load 方法先于子類的 +load 方法調(diào)用朽褪,類本身的 +load 方法先于分類的 +load 方法調(diào)用置吓。

為剛才的類添加+load方法實(shí)現(xiàn)

@implementation SubClass

+ (void)initialize {
    NSLog(@"%s", __FUNCTION__);
}

+ (void)load {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

@end

@implementation SubClass (test)

+ (void)initialize {
    NSLog(@"%s", __FUNCTION__);
}

+ (void)load {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

@end

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    NSLog(@"didFinishLaunchingWithOptions");
    return YES;
}

打印順序?yàn)?/p>

+[BaseClass initialize]
BaseClass +[BaseClass load]
+[SubClass(test) initialize]
SubClass +[SubClass load]
SubClass +[SubClass(test) load]
didFinishLaunchingWithOptions

從這個例子可以出,load方法不會被分類的+load覆蓋缔赠,父類的load方法先調(diào)用衍锚,load方法在didFinishLaunchingWithOptions執(zhí)行前調(diào)用。

回到最開始的例子嗤堰,

@implementation BaseClass

+ (void)initialize {
//    NSLog(@"%@ , %s", [self class], __FUNCTION__);
    NSLog(@"%s", __FUNCTION__);
}
@end
@implementation SubClass

+ (void)load {
    NSLog(@"%@ %s", [self class], __FUNCTION__);
}

@end

SubClass *class = [[SubClass alloc] init];

打印順序

+[BaseClass initialize]
+[BaseClass initialize]
SubClass +[SubClass load]

驗(yàn)證了當(dāng)子類沒有實(shí)現(xiàn)initialize時戴质,父類的initialize調(diào)用了2次且 +initialize 的調(diào)用在 +load 調(diào)用之前,這是因?yàn)槲覀冊?+load 實(shí)現(xiàn)中包含 [self class] 的調(diào)用踢匣。

到這里告匠,+ initialize+load的調(diào)用順序與特性就驗(yàn)證的差不多了。那么知道了這些在開發(fā)的時候具體有什么用處呢离唬?

實(shí)際案例

+load案例

load使用示例1, 見 @sunnyxx 大神的博客 Notification Once, 用于給 AppDelegate 瘦身后专。
load使用示例2, 見博客 Method Swizzling 和 AOP 實(shí)踐, 在UIViewController的 +load 時期執(zhí)行IMP替換,實(shí)現(xiàn)AOP输莺。

+ initialize案例

使用initialize與static實(shí)現(xiàn)單例模式:

static SingleModel *initTest = nil;

@implementation SingleModel

+ (void)initialize
{
    NSLog(@"InitTest : initialize className : %@",[self class]);
    if (initTest == nil) {
        initTest = [[SingleModel alloc] init];
    }
}
+ (SingleModel *)defaultManager
{
    return initTest;
}

@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    SingleModel *single1 = [SingleModel defaultManager];
    SingleModel *single2 = [SingleModel defaultManager];
    SingleModel *single3 = [SingleModel defaultManager];
    NSLog(@"single1 %p",single1);
    NSLog(@"single2 %p",single2);
    NSLog(@"single3 %p",single3);
}
@end

打印結(jié)果

single1 0x60000000fb10
single2 0x60000000fb10
single3 0x60000000fb10

都訪問的同一塊內(nèi)存戚哎,只初始化了一次。
同理我們也可以借助initialize初始化一些全局變量嫂用,靜態(tài)變量型凳。

最后再補(bǔ)充一下:load和initialize方法內(nèi)部使用了鎖,因此它們是線程安全的嘱函。實(shí)現(xiàn)時要盡可能保持簡單甘畅,避免阻塞線程,不要再使用鎖。

相關(guān)文章

Objective-C類初始化:load與initialize
NSObject +load and +initialize - What do they do?
iOS初探+load和+initialize

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疏唾,一起剝皮案震驚了整個濱河市蓄氧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荸实,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缴淋,死亡現(xiàn)場離奇詭異准给,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)重抖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門露氮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人钟沛,你說我怎么就攤上這事畔规。” “怎么了恨统?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵叁扫,是天一觀的道長。 經(jīng)常有香客問我畜埋,道長莫绣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任悠鞍,我火速辦了婚禮对室,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘咖祭。我一直安慰自己掩宜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布么翰。 她就那樣靜靜地躺著牺汤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪浩嫌。 梳的紋絲不亂的頭發(fā)上慧瘤,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天,我揣著相機(jī)與錄音固该,去河邊找鬼锅减。 笑死,一個胖子當(dāng)著我的面吹牛伐坏,可吹牛的內(nèi)容都是我干的怔匣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼每瞒!你這毒婦竟也來了金闽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤剿骨,失蹤者是張志新(化名)和其女友劉穎代芜,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浓利,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挤庇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了贷掖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嫡秕。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖苹威,靈堂內(nèi)的尸體忽然破棺而出昆咽,到底是詐尸還是另有隱情,我是刑警寧澤牙甫,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布掷酗,位于F島的核電站,受9級特大地震影響窟哺,放射性物質(zhì)發(fā)生泄漏汇在。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一脏答、第九天 我趴在偏房一處隱蔽的房頂上張望糕殉。 院中可真熱鬧,春花似錦殖告、人聲如沸阿蝶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽羡洁。三九已至,卻和暖如春爽丹,著一層夾襖步出監(jiān)牢的瞬間筑煮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工粤蝎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留真仲,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓初澎,卻偏偏與公主長得像秸应,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容