NSObject 的 initialize 和 load 方法

做爲 NSObject 類中的 2 個方法 initialize 和 load 一直被咱們所熟知,可是又沒有具體去深刻的瞭解,今天結合 Apple 官方文檔,咱們來深刻了解一下 initialize 和 load 方法 。objective-c

initialize

看文檔能夠得知 initialize 方法的做用是在 class 收到第一個消息以前初始化 class。安全

Initializes the class before it receives its first message.

+ (void)initialize;
複製代碼
  1. runtime 會在程序的 class 收到第一個消息以前給每個 class 發送 initialize 消息,讓 class 進行初始化。
  2. Superclasses 會在 Subclasses 以前收到 initialize 消息。
  3. initialize 方法是線程安全的,在 initialize 方法運行期間, class會被鎖定,其餘的線程沒法向該 class 發送消息,因此咱們儘可能避免在 initialize 方法裏面作複雜的實現。

接下來咱們用代碼來探究 initialize 這個方法,咱們有 3 個 class ,分別是 Man,Woman,Person。 其中 Man 和 Woman 都繼承自 Person 。app

#import <Foundation/Foundation.h>

@interface Person : NSObject

@end


#import "Person.h"

@implementation Person
+(void)initialize{
    NSLog(@"call person initialize");
}

@end
複製代碼
#import "Person.h"

@interface Man : Person

@end

#import "Man.h"

@implementation Man

@end

複製代碼
#import "Person.h"

@interface Woman : Person

@end

#import "Woman.h"

@implementation Woman

@end

複製代碼

在 main.m 方法代碼以下:ui

#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
    }
    return 0;
}
複製代碼

咱們運行程序獲得以下輸出this

call person initialize
複製代碼

在 Person 的 initialize 方法設置斷點,查看堆棧調用,Person 調用的第一個方法確實是 initialize ,該方法在 Person 收到第一個消息以前初始化 Person。spa

image.png

接下來修改 main.m 的代碼,同時生成 Person ,Man,Woman 的對象實例。線程

#import <Foundation/Foundation.h>
#import "Person.h"
#import "Man.h"
#import "Woman.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        Man *m = [[Man alloc] init];
        Woman *w = [[Woman alloc] init];
    }
    return 0;
}
複製代碼

運行程序,查看控制檯輸出code

call person initialize
 call person initialize
 call person initialize
複製代碼

咱們能夠看出若是子類沒有實現 initialize 方法,runtime 會調用父類的 initialize 實現,那麼咱們就不用在子類中調用 [super initialize] ,同時也意味着父類的 initialize 會被屢次調用,那麼咱們能夠採用以下的方式來避免這個問題。orm

+ (void)initialize {
  if (self == [ClassName self]) {
    // ... do the initialization ...
  }
}
複製代碼

那若是 Person子類 Man 和 Woman 都實現 initialize 方法呢?cdn

@implementation Man
+(void)initialize{
    NSLog(@"call man initialize");
}

+(void)initialize{
    NSLog(@"call woman initialize");
}
@end


複製代碼

運行程序,查看控制檯輸出,能夠看到 class 都是調用各自的 initialize 方法實現。

call person initialize
call man initialize
call woman initialize
複製代碼

每一個 class 有且僅有一次 initialize 方法調用,若是想要實現 class 和 category 的分別獨立初始化,咱們應該使用 load 方法。

load

看文檔能夠得知 class 或者 category 被添加到 runtime 的時候,load 方法就會被調用。

Invoked whenever a class or category is added to the Objective-C runtime; 
implement this method to perform class-specific behavior upon loading.

+ (void)load;
複製代碼
  1. 和 initialize 方法相似,class 的 load 方法會在 superlcasses 的 load 方法調用以後被調用。
  2. category 的 load 方法會在 class 的 load 方法調用以後被調用。

接下來咱們用代碼來探究 load 這個方法,仍是用以前的例子,咱們有 3 個 class ,分別是 Man,Woman,Person。 其中 Man 和 Woman 都繼承自 Person 。

Person 類實現了 load 方法

//Person.m
#import "Person.h"

@implementation Person

+(void)initialize{
    NSLog(@"call person initialize");
}

+(void)load{
    NSLog(@"call person load");
}

// main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Man.h"
#import "Woman.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];

    }
    return 0;
}

@end
複製代碼

運行程序,查看控制檯輸出,能夠看出 load 方法調用在 initialize 方法以前。

call person load
call person initialize
複製代碼

接下來修改 main.m 實現

#import <Foundation/Foundation.h>
#import "Person.h"
#import "Man.h"
#import "Woman.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        Man *m = [[Man alloc] init];
        Woman *w = [[Woman alloc] init];
    }
    return 0;
}

複製代碼

運行程序,查看控制檯輸出,能夠看出子類沒有實現 load 方法的時候,runtime 不會自動調用父類的 load 方法實現

call person load
call person initialize
call man initialize
call woman initialize
複製代碼

接下來修改 Man.m 和 Woman.m 實現

// Man.m
#import "Man.h"

@implementation Man
+(void)initialize{
    NSLog(@"call man initialize");
}

+(void)load{
    NSLog(@"call man load");
}
@end

// Woman.m
#import "Woman.h"

@implementation Woman

+(void)initialize{
    NSLog(@"call woman initialize");
}

+(void)load{
    NSLog(@"call woman load");
}

@end
複製代碼

運行程序,查看控制檯輸出,能夠看出,咱們沒有在 Woman 和 Man 中顯式調用父類的 load 方法,可是父類的 load 方法調用都在子類以前,這個和 initialize 方法是同樣的,畢竟是要先有父類初始化,纔會有子類初始化。

call person load
call woman load
call man load
call person initialize
call man initialize
call woman initialize
複製代碼

接下來新建一個 Man 的 category 叫作 Man(Work),

// Mam + Work.m
#import "Man+Work.h"
@implementation Man (Work)
+(void)load{
    NSLog(@"call Man (Work) load");
}
@end
複製代碼

運行程序,查看控制檯輸出,能夠看出 category 的 load 方法調用老是在 class 的 load 方法調用以後。

call man load
call Man (Work) load
call man initialize
複製代碼

總結

經過一些代碼例子和官方文檔,咱們能夠知道 initialize 方法的做用是在 class 收到第一個消息以前初始化 class。load 方法的調用時機是在 class 或者 category 被添加到 runtime 的時候。load 方法調用在 initialize 方法以前。

參考

  1. https://developer.apple.com/documentation/objectivec/nsobject/1418639-initialize?preferredLanguage=occ
  2. https://developer.apple.com/documentation/objectivec/nsobject/1418815-load?language=objc
  3. http://zhangbuhuai.com/initialize-and-load-in-objective-c/
相關文章
相關標籤/搜索