1. ホーム
  2. objective-c

[解決済み] NSObject +loadと+initialize - これらは何をするのですか?

2022-08-31 22:34:03

質問

私は、開発者が +initialize または +load をオーバーライドする状況を理解することに興味があります。ドキュメントでは、これらのメソッドが Objective-C ランタイムによって呼び出されることは明らかですが、これらのメソッドのドキュメントから明らかなのは、本当にそれだけです。)

私の好奇心は、Apple のサンプルコードである MVCNetworking を見たことから来ました。彼らのモデル クラスには +(void) applicationStartup メソッドを持っています。これは、ファイルシステム上でいくつかのハウスキーピングを行い、NSDefaults を読み取ります。

私は MVCNetworking プロジェクトを修正し、App Delegate の +applicationStartup への呼び出しを削除し、掃除のビットを +load に入れました。私は、+load または +initialize に対して呼び出さなければならないカスタム セットアップ メソッドに関する微妙な点、gotchas、および whatnots について理解を得ることを望んでいます。


For +load ドキュメントにはこうあります。

loadメッセージはクラスとカテゴリに送られます。 動的にロードされ、静的にリンクされたクラスとカテゴリに送られます。 クラスまたはカテゴリが応答することができるメソッドを実装している場合にのみ送信されます。

この文章は、すべての単語の正確な意味を知らなければ、解析するのが困難なクドイ文章です。助けてください。

  • 動的ロードと静的リンクの両方とはどういう意味ですか。

  • 新しく読み込まれたクラスまたはカテゴリは、応答することができるメソッドを実装しています。どのように応答するのですか?


+initializeについては、ドキュメントに書かれています。

initializeはクラスごとに一度だけ実行されます。もし クラスと、そのクラスのカテゴリに対して独立した初期化を行いたい場合は のカテゴリに対して独立した初期化を行いたい場合は、loadメソッドを実装する必要があります。

これは、"クラスを設定しようとしている場合... initialize."を使用しないでください、という意味だと解釈しています。では、いつ、なぜ、initializeをオーバーライドするのでしょうか?

どのように解決するのですか?

その load メッセージ

ランタイムが送信する load メッセージを各クラスオブジェクトに送ります。クラスオブジェクトがプロセスのアドレス空間にロードされるとすぐにです。 プログラムの実行可能ファイルの一部であるクラスに対して、ランタイムは load メッセージを送信します。 共有(動的にロードされる)ライブラリにあるクラスについては、ランタイムは共有ライブラリがプロセスのアドレス空間にロードされた直後にロードメッセージを送信します。

さらに、ランタイムは load をクラスオブジェクトに送るのは、そのクラスオブジェクト自身が load メソッドを実装している場合、そのクラスオブジェクトに 例

@interface Superclass : NSObject
@end

@interface Subclass : Superclass
@end

@implementation Superclass

+ (void)load {
    NSLog(@"in Superclass load");
}

@end

@implementation Subclass

// ... load not implemented in this class

@end

ランタイムは load メッセージに Superclass クラスオブジェクトに送信します。 それは ではなく を送信します。 load メッセージに Subclass クラスオブジェクトに、たとえ Subclass からメソッドを継承していても Superclass .

ランタイムは load を送信した後、クラスオブジェクトに load メッセージを送信した後 (そのスーパークラスのオブジェクトが load を実装している場合)、およびリンク先の共有ライブラリのすべてのクラス・オブジェクトにメッセージを送信します。 しかし、自分の実行ファイル内の他のどのクラスが load を受け取ったかどうかはわかりません。

あなたのプロセスがそのアドレス空間にロードするすべてのクラスは、そのアドレス空間に load を実装している場合、そのクラスは load メソッドを実装していれば、プロセスがそのクラスを他に利用するかどうかに関わらず

ランタイムがどのように load メソッドの中で特別なケースとして _class_getLoadMethod objc-runtime-new.mm から直接呼び出す。 call_class_loads objc-loadmethod.mm .

また、ランタイムは load を実装している同じクラスの複数のカテゴリであっても、 ロードするすべてのカテゴリの メソッドを実行します。 load .  これは珍しいことです。  通常、2つのカテゴリが同じクラスで同じメソッドを定義した場合、1つのメソッドが「勝って」使用され、もう1つのメソッドは決して呼び出されることはないでしょう。

initialize メソッド

ランタイムは initialize メソッドをクラスオブジェクトに呼び出します(最初のメッセージを送信する直前の load または initialize ) をクラスオブジェクトやクラスのインスタンスに送信します。 このメッセージは通常のメカニズムで送信されるので、もしあなたのクラスが initialize を実装しておらず、実装しているクラスを継承している場合、 あなたのクラスはそのスーパークラスの initialize . ランタイムは initialize をクラスのスーパークラス全てに送ります (スーパークラスがまだ initialize ).

@interface Superclass : NSObject
@end

@interface Subclass : Superclass
@end

@implementation Superclass

+ (void)initialize {
    NSLog(@"in Superclass initialize; self = %@", self);
}

@end

@implementation Subclass

// ... initialize not implemented in this class

@end

int main(int argc, char *argv[]) {
    @autoreleasepool {
        Subclass *object = [[Subclass alloc] init];
    }
    return 0;
}

このプログラムは2行の出力を行います。

2012-11-10 16:18:38.984 testApp[7498:c07] in Superclass initialize; self = Superclass
2012-11-10 16:18:38.987 testApp[7498:c07] in Superclass initialize; self = Subclass

システムは initialize メソッドを遅延して送信するため、プログラムが実際にクラス (またはサブクラス、あるいはクラスやサブクラスのインスタンス) にメッセージを送信しない限り、クラスはメッセージを受信しません。 そして、受信するまでに initialize を受け取る頃には、あなたのプロセス内のすべてのクラスはすでに load を受け取っているはずです (適切であれば)。

を実装する標準的な方法は initialize はこうです。

@implementation Someclass

+ (void)initialize {
    if (self == [Someclass class]) {
        // do whatever
    }
}

このパターンのポイントは Someclass を実装していないサブクラスがある場合、それ自体を再初期化することを避けるためです。 initialize .

ランタイムは initialize メッセージを _class_initialize 関数で objc-initialize.mm . を使用していることがわかります。 objc_msgSend を使って送信していることがわかります。これは通常のメッセージ送信機能です。

さらに読む

チェックアウト マイク・アッシュの金曜日のQ&A をご覧ください。