Apple の Objective C ランタイム ガイドでは、独自のコードで objc_msgSend() を使用してはならないと述べており、代わりに methodForSelector: を使用することを推奨しています。ただし、これには理由がありません。
コードで objc_msgSend() を呼び出すことの危険性は何ですか?
Apple の Objective C ランタイム ガイドでは、独自のコードで objc_msgSend() を使用してはならないと述べており、代わりに methodForSelector: を使用することを推奨しています。ただし、これには理由がありません。
コードで objc_msgSend() を呼び出すことの危険性は何ですか?
コンパイラは、objc_msgSend()
Objective-C メッセージング式に遭遇すると、への呼び出し (またはその変形) を自動的に生成します。コンパイル時に送信されるクラスとセレクターがわかっている場合は、書く理由はありません
id obj = objc_msgSend(objc_msgSend([NSObject class], @selector(alloc)), @selector(init));
それ以外の
id obj = [[NSObject alloc] init];
クラスまたはセレクター (またはその両方) を知らなくても、正しく型指定された関数ポインターを取得する方が安全です (少なくとも、コンパイラーは、潜在的に厄介な/間違ったことをしている場合に警告する機会があります)。実装自体を変更し、代わりにその関数ポインターを使用します。
const char *(*fptr)(NSString *, SEL) = [NSString instanceMethodForSelector:@selector(UTF8String)];
const char *cstr = fptr(@"Foo");
objc_msgSend()
これは、メソッドの引数の型がデフォルトの昇格の影響を受けやすい場合に特に当てはまります。そうである場合は、プログラムが未定義の動作をすぐに呼び出すため、可変引数引数を介してそれらを渡したくありません。
#1の「またはその変形」の部分に注意してください。すべてのメッセージ送信でobjc_msgSend()
関数自体が使用されるわけではありません。ABI の複雑さと要件 (特に関数の呼び出し規約) により、浮動小数点値や構造体などを返すための別の関数があります。たとえば、何らかの検索 (部分文字列など) を実行し、NSRange
構造を返すメソッドの場合、プラットフォームによっては、メッセンジャー関数の構造を返すバージョンを使用する必要がある場合があります。
NSRange retval;
objc_msgSend_stret(&retval, @"FooBar", @selector(rangeOfString:), @"Bar");
これを間違えると (たとえば、不適切なメッセンジャー関数を使用したり、戻り値へのポインタと へのポインタを取り違えたりするself
など)、プログラムが正しく動作しないか、クラッシュする可能性があります。(そして、それはそれほど単純ではないので、おそらく間違っているでしょうstruct
-小さな構造体は1つまたは2つのプロセッサレジスタに収まり、そのため、筋金入りの ABI ハッカーでない限り、コンパイラにその仕事をさせたいと思うでしょう。
私はケースを作ることができます。クロスプラットフォーム プロジェクト (Windows、Mac、Linux) の C++ ファイルの 1 つ (ARC に切り替える前) で msgSend を使用しました。これを使用して、後でフロントエンドからバックエンドに、またはその逆に移動するために使用される、バッキング (共有コード) 内の参照をカウントします。確かに、非常に特殊なケースです。