0

NSInvocationが必要かどうかを数日間考えていましたNSMethodSignature。独自の NSInvocation を書きたいとしましょう。私の要件は次のようになります。

  1. セレクターが欲しいSEL
  2. セレクターを呼び出すターゲット オブジェクト
  3. 引数配列

次にIMP、ターゲットと から outを取得し、パラメータとしてSEL渡しargumentます。

それで、私の質問は、なぜNSMethodSignatureを構築して使用するのにが必要なのNSInvocationですか?

SEL注: aと targetしかないので、このメソッドの引数と戻り値の型がないことはわかっていますが、なぜ引数と戻り値の型を気にするのでしょうか?

4

2 に答える 2

3

C の各型のサイズは異なります。(同じタイプでも、システムによってサイズが異なる場合がありますが、ここでは無視します。)intシステムに応じて、32 ビットまたは 64 ビットを持つことができます。double64 ビットを使用します。charは 8 ビットを表します (ただしint、システムの引き渡し規則によっては通常として渡される場合があります)。そして最後に、そして最も重要なことは、struct型には、含まれる要素の数とそれぞれのサイズに応じて、さまざまなサイズがあります。それがどれだけ大きくなるかに制限はありません。したがって、型に関係なく同じ方法で引数を渡すことは不可能です。したがって、呼び出し元の関数が引数を配置する方法、および呼び出された関数がその引数を解釈する方法は、関数のシグネチャに依存する必要があります。(型にとらわれない「引数配列」を持つことはできません。配列要素のサイズはどうなりますか?) 通常の関数呼び出しがコンパイルされると、コンパイラはコンパイル時にシグネチャを認識し、呼び出し規則に従って正しく配置できます。ただしNSInvocation、実行時の呼び出しを管理するためのものです。したがって、機能するにはメソッド シグネチャの表現が必要です。

でできることはいくつかありNSInvocationます。これらのそれぞれには、パラメーターの数と型 (少なくとも型のサイズ) に関する知識が必要です。

  1. メソッドを持たないオブジェクトにメッセージが送信されると、ランタイムはNSInvocationオブジェクトを作成して に渡します-forwardInvocation:。このNSInvocationオブジェクトには、渡されたすべての引数のコピーが含まれています。これは、格納して後で呼び出すことができるためです。したがって、ランタイムは、レジスターやスタック (呼び出し規約での引数の配置方法に応じて) から適切な量のデータをコピーするために、少なくともパラメーターの合計の大きさを知る必要があります。NSInvocation物体。
  2. オブジェクトがあるNSInvocation場合、 を使用して i 番目の引数の値を照会できます-getArgument:atIndex:。を使用して、i 番目の引数の値を設定/変更することもできます-setArgument:atIndex:。これには、1) データ バッファーのどこで i 番目のパラメーターが始まるかを知る必要があります。これには、前のパラメーターの大きさを知る必要があり、2) 適切な量のデータをコピーできるように、i 番目のパラメーターの大きさを知る必要があります (コピーが少なすぎると、値が壊れます。コピーしすぎると、値が破損します)。たとえば、 を実行するとgetArgument、指定したバッファが上書きされる可能性があります; または を実行するとsetArgument、他の引数が上書きされます)。
  3. do-retainArgumentsを指定すると、オブジェクト ポインター型のすべての引数が保持されます。これには、オブジェクト ポインター型とその他の型を区別する必要があるため、型情報にはサイズだけでなく含める必要があります。
  4. を呼び出すことができますNSInvocation。これにより、メソッドへの呼び出しが構築および実行されます。これには、少なくとも、関数が期待する場所にすべてのデータを配置するために、バッファーからレジスター/スタックにコピーするデータの量を知る必要があります。これには、少なくともすべてのパラメーターの合計サイズを知る必要があり、おそらく個々のパラメーターのサイズも知る必要があるため、レジスタのパラメーターとスタックのパラメーターの分割を正しく把握できます。
  5. -getReturnValue:;を使用して、呼び出しの戻り値を取得できます。これには、上記の引数の取得と同様の問題があります。

    • 上記で言及されていないことは、戻り値の型も呼び出しメカニズムに大きな影響を与える可能性があるということです。Objective-C の一般的なアーキテクチャである x86 と ARM では、戻り値の型がstruct型の場合、呼び出し規約が大きく異なります。事実上、すべての通常のパラメーターの前に追加の (最初の) パラメーターが追加されます。構造体の結果を書き込むスペース。これは、結果がレジスタに返される通常の呼び出し規則の代わりです。(PowerPC では、double戻り値の型も特別に扱われると思います。) したがって、戻り値の型を知ることは、基本的にNSInvocation.
于 2013-09-06T08:37:01.363 に答える
1

NSMethodSignature は、メッセージの送信および転送メカニズムが呼び出しに対して適切に機能するために必要です。NSMethodSignature と NSInvocation は のラッパーとして構築されました__builtin_call()。これは、アーキテクチャに依存し、特定の関数が必要とするスタック領域について非常に保守的です。したがって、呼び出しが呼び出されると、__builtin_call()必要なすべての情報をメソッド シグネチャから取得し、スタックが再検索する方法に関する適切な情報も受信することを認識して転送メカニズムに呼び出しをスローすることで、正常に失敗する可能性があります。呼び出し。

そうは言っても、配列を VARARGS に変換することをサポートするように C 言語を変更せずに、メソッド シグネチャなしでプリミティブ NSInvocation を作成することはできませんobjc_msgSend()。それを回避できたとしても、引数のサイズと戻り値の型を計算する必要があります (それほど難しくはありませんが、間違っている場合は大きな間違いです)、 への適切な呼び出しを管理する必要があります__builtin_call()。これには、メッセージ送信アーキテクチャまたは ffi に関する詳細な知識が必要です (これは、おそらくいずれにせよ低下し__builtin_call()ます)。

于 2013-09-06T00:56:29.263 に答える