タイトルが言うように私の質問.明らかに、最初のパラメーターはこのポインターに使用されました.c ++のいくつかの味で.2番目のパラメーターはどうですか? ありがとう。
3 に答える
の署名objc_msgSend()
は次のとおりです。
id objc_msgSend(id self, SEL op, ...);
すべてのメソッド呼び出しは、この関数への呼び出しにコンパイルされます。つまり、次のように呼び出す場合:
[anArray objectAtIndex:42];
それは次のようにコンパイルされます。
objc_msgSend(anArray, @selector(objectAtIndex:), 42);
さて、あなたの質問に対して、メソッドが 2 番目の引数として SEL を持つ関数にコンパイルされるのはなぜですか。または、より具体的には、なぜこの方法なのか:
- (id)objectAtIndex:(NSUInteger)index;
次の C 関数とまったく同じです。
id object_at_index(id object, SEL _cmd, NSUInteger index);
答えはスピードスピード スピードです。
スピード
具体的には、これを行うことでobjc_msgSend()
、スタック フレーム*を書き換える必要がなくなり、末尾呼び出しの最適化を使用してメソッド呼び出しに直接ジャンプすることもできます。これは、デバッガーでバックトレースが表示されないのと同じ理由ですobjc_msgSend()
(メッセンジャーで実際にクラッシュ/ブレークした場合を除いて)。
objc_msgSend()
とを使用しobject
て_cmd
メソッドの実装を検索し、文字通り、その実装にジャンプします。
とても早い。スタック フレームはそのままです。
また、他の人が述べているよう_cmd
に、メソッドの実装を使用すると、さまざまな理由で便利です。同様に、メッセンジャーが NSInvocation を介したプロキシ サポートなどの巧妙なトリックを実行できることも意味します。
*スタック フレームの書き換えは、非常に複雑でコストがかかる可能性があります。いくつかの引数は、時々レジスターにあるかもしれません...すべてのアーキテクチャに依存するABIの厄介さ。のようなものを書く上での最大の課題の 1 つは、スタックに触れずimp_implementationWithBlock()
にそうする方法を考え出すことでした。
2 番目のパラメーターにセレクターを含める目的は、共通のディスパッチ メカニズムを有効にすることです。そのため、メソッド ディスパッチ コードは常に 2 番目のパラメーターがセレクターであると想定し、それに基づいてディスパッチするか、継承チェーンをたどるか、さらには を作成してNSInvocation
を呼び出しますforwardInvocation:
。
一般に、システム レベルのルーチンのみがセレクター引数を使用しますが、例外が発生した場合や、デバッガーで使用している場合にどのルーチンが問題を引き起こしているかを把握しようとしている場合は、セレクター引数を使用すると便利です。forwardInvocation
ドキュメントから:
討論
このデータ型は、メソッドを実装する関数の先頭へのポインターです。この関数は、現在の CPU アーキテクチャに実装されている標準の C 呼び出し規則を使用します。最初の引数は、self へのポインター (つまり、このクラスの特定のインスタンスのメモリ、またはクラス メソッドの場合はメタクラスへのポインター) です。2 番目の引数はメソッド セレクターです。メソッドの引数が続きます。
Objective-C では、メソッドを呼び出すときに、ターゲット、セレクター、および最終的な引数を知る必要があります。これを手動で行おうとしているとしましょう: セレクターがわからない場合、どのメソッドを呼び出すべきかをどうやって知ることができますか? ランダムメソッドを呼び出しますか?いいえ、メソッド名を知っているので、正しいメソッドを呼び出します。