web-dev-qa-db-ja.com

Objective Cでobjc_msgSend()を使用すべきではないのはなぜですか?

AppleのObjectiveCランタイムガイドには、独自のコードでobjc_msgSend()を使用してはならないと記載されており、代わりにmethodForSelector:を使用することをお勧めします。ただし、これには理由がありません。

コードでobjc_msgSend()を呼び出すことの危険性は何ですか?

22
cfischer

理由#1:スタイルが悪い-冗長で読めない。

コンパイラは、Objective-Cメッセージング式に遭遇すると、objc_msgSend()(またはそのバリアント)への呼び出しを自動的に生成します。コンパイル時に送信されるクラスとセレクターがわかっている場合は、書く理由はありません。

_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()が取る可変引数にそれらを渡したくありません。未定義の動作を呼び出します。

理由#2:危険でエラーが発生しやすい。

#1の「またはその変形」の部分に注意してください。すべてのメッセージ送信がobjc_msgSend()関数自体を使用するわけではありません。 ABIの複雑さと要件(特に関数の呼び出し規約)により、浮動小数点値や構造体などを返すための個別の関数があります。たとえば、ある種の検索(部分文字列など)を実行し、NSRange構造体を返すメソッドの場合、プラットフォームによっては、構造体を返すバージョンを使用する必要がある場合があります。メッセンジャー機能の:

_NSRange retval;
objc_msgSend_stret(&retval, @"FooBar", @selector(rangeOfString:), @"Bar");
_

また、これを間違えると(たとえば、不適切なメッセンジャー関数を使用したり、戻り値やselfへのポインターを混同したりするなど)、プログラムが正しく動作しなかったり、クラッシュしたりする可能性があります。 (そしておそらくあなたはそれを間違えるでしょうそれはそれほど単純ではないので-小さな構造は1つまたは2つのプロセッサレジスタに収まるので、structを返すすべてのメソッドがこのバリアントを使用するわけではありません、戻り値の場所としてスタックを使用する必要がなくなります。そのため、ハードコアABIハッカーでない限り、コンパイラにその仕事を任せたい、またはドラゴンがいるのです。)

38
user529758

あなたは「危険は何ですか?」と尋ねます。そして@ H2CO3は、「あなたが筋金入りのABIハッカーでない限り」で終わるものをいくつかリストしています...

多くのルールと同様に、例外があります(ARCではさらにいくつかのルールがあります)。したがって、msgSendを使用する理由は、次のようになります。

[1] msgSend-使用しないでください

[2]しかし、私はここにケースがあります...-おそらくそうではないでしょう、別の解決策を探し続けてください

.。

[10]私は本当にここでそれを使うべきだと思います-もう一度考えてください

.。

[100]本当に、これはmsgSendの場合のように見えます、私は他の解決策を見ることができません! OK、読んでくださいDocument.m in Appleのテキストエディットコードサンプル 。なぜ彼らがmsgSendを使用したのか知っていますか?よろしいですか...もう一度考えてください...

.。

[1000] Appleがそれを使用した理由を理解しました、そして私の場合も同様です...あなたは見つけて理解しました例外を証明しますルールとあなたのケースが一致します、それを使用してください

HTH

10
CRD

私は主張することができます。クロスプラットフォームプロジェクト(Windows、Mac、Linux)にあるC++ファイルの1つ(ARCに切り替える前)でmsgSendを使用しました。これを使用して、後でフロントエンドからバックエンドに、またはその逆に移動するために使用される、バック(共有コード)内の参照を参照カウントします。確かに、非常に特殊なケースです。

4
Mike Lischke