-performSelectorの使用:メソッドを呼び出すだけの場合


回答:


191

基本的に、performSelectorを使用すると、指定したオブジェクトのセレクターを呼び出すセレクターを動的に決定できます。つまり、ランタイムの前にセレクターを決定する必要はありません。

したがって、これらは同等ですが:

[anObject aMethod]; 
[anObject performSelector:@selector(aMethod)]; 

2番目の形式では、これを行うことができます。

SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];

メッセージを送信する前に。


3
実際にfindTheAppropriateSelectorForTheCurrentSituation()の結果をaSelectorに割り当ててから、[anObject performSelector:aSelector]を呼び出すことを指摘する価値があります。@selectorはSELを生成します。
Daniel Yankowsky

4
使用performSelector:は、クラスにtarget-actionを実装する場合にのみ行うものです。兄弟performSelectorInBackground:withObject:performSelectorOnMainThread:withObject:waitUntilDone:は、しばしばより便利です。バックグラウンドスレッドを生成し、バックグラウンドスレッドからメインスレッドに結果をコールバックします。
PeyloW

2
performSelectorコンパイル警告を抑制するのにも役立ちます。メソッドが存在することがわかっている場合(を使用した後などrespondsToSelector)、Xcodeが「応答しない可能性があります」と表示されなくなりyour_selectorます。警告の本当の原因を見つける代わりに、それ使用しないでください。;)
Marc

StackOverflowの他のスレッドを読んで、performSelectorの使用は恐ろしいデザインの反映であり、大量の評価があったことを読みました。もう一度見つけられたらいいのに。私はグーグルを検索し、結果をstackoverflowに制限し、18,000の結果を得ました。えww。
Logicsaurus Rex

「恐ろしいデザインの反映」は過度に単純化されています。これは、ブロックが利用可能になる前に私たちが持っていたものであり、当時も現在もすべての用途が悪いわけではありません。これでブロック利用可能になりましたが、非常に単純なことをしているのでない限り、それはおそらく新しいコードのより良い選択です。
イーサン、

16

質問のこの非常に基本的な例では、

[object doSomething];
[object performSelector:@selector(doSomething)]; 

何が起きるかに違いはありません。doSomethingはオブジェクトによって同期的に実行されます。"doSomething"だけが非常に単純なメソッドであり、何も返さず、パラメーターを必要としません。

それは次のようなもう少し複雑なものでしたか?

(void)doSomethingWithMyAge:(NSUInteger)age;

[object doSomethingWithMyAge:42];なので、状況は複雑になります。

パラメータを持つすべてのバリアントはオブジェクトパラメータのみを受け入れるため、「performSelector」のバリアントで呼び出すことはできなくなりました。

ここでのセレクターは "doSomethingWithMyAge:"ですが、

[object performSelector:@selector(doSomethingWithMyAge:) withObject:42];  

単にコンパイルされません。42ではなくNSNumber:@(42)を渡しても、メソッドではオブジェクトではなく基本的なCタイプが想定されるため、効果がありません。

さらに、performSelectorバリアントには最大2つのパラメーターがあり、それ以上はありません。メソッドには多くの場合、さらに多くのパラメーターがあります。

私は、performSelectorの同期バリアントですが、

- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

常にオブジェクトを返します。単純なBOOLまたはNSUIntegerも返すことができました。

前の回答で説明したように、performSelectorの2つの主な用途の1つは、実行するメソッドの名前を動的に作成することです。例えば

 SEL method = NSSelectorFromString([NSString stringWithFormat:@"doSomethingWithMy%@:", @"Age");
[object performSelector:method];

もう1つは、現在の実行ループで後で実行される、メッセージをオブジェクトに非同期でディスパッチすることです。このために、他にいくつかのperformSelectorバリアントがあります。

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
- (void)performSelector:(SEL)aSelector target:(id)target argument:(id)arg order:(NSUInteger)order modes:(NSArray *)modes;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;

(はい、NSThread、NSRunLoop、NSObjectなどのいくつかのFoundationクラスのカテゴリから収集しました)

各バリアントには独自の特別な動作がありますが、すべてが共通して何かを共有します(少なくともwaitUntilDoneがNOに設定されている場合)。「performSelector」呼び出しはすぐに返され、オブジェクトへのメッセージはしばらくしてから現在の実行ループにのみ配置されます。

実行が遅延するため、当然、セレクターのメソッドから返される戻り値はありません。したがって、これらすべての非同期バリアントでは-(void)戻り値が返されます。

なんとかカバーしてくれたらいいのに...


12

@ennuikillerが登場しました。基本的に、動的に生成されたセレクターは、コードをコンパイルするときに呼び出すメソッドの名前がわからない(通常はわからない)場合に役立ちます。

主な違いの1つは、0から2のパラメーターを持つメソッドで使用するように設計されているという点で、-performSelector:マルチスレッドや遅延のバリアントを含む)友人は多少制限されていることです。たとえば、-outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation:6つのパラメーターを使用して呼び出し、それを返すのNSStringは非常に扱いにくく、提供されたメソッドではサポートされていません。


5
そのためには、NSInvocationオブジェクトを使用する必要があります。
Dave DeLong

6
もう1つの違い:performSelector:フレンドはすべてオブジェクト引数を取ります。つまりsetAlphaValue:、その引数は浮動小数点であるため、それらを使用して(たとえば)を呼び出すことはできません。
チャック

4

セレクターは、他の言語の関数ポインターに少し似ています。コンパイル時にどのメソッドを実行時に呼び出すかわからない場合に使用します。また、関数ポインターと同様に、呼び出しの動詞部分のみをカプセル化します。メソッドにパラメータがある場合は、それらも渡す必要があります。

アンは、NSInvocationそれが一緒に多くの情報を結合することを除いて、同様の目的を果たします。動詞部分だけでなく、ターゲットオブジェクトとパラメーターも含まれます。これは、現在ではなく将来、特定のパラメーターを使用して特定のオブジェクトのメソッドを呼び出す場合に便利です。適切なものNSInvocationを作成して、後で起動できます。


5
関数セレクターは引数で呼び出すことができるものであり、セレクターを使用してそれを実装するオブジェクトの特定のメソッドを呼び出すことができるという点で、セレクターは実際には関数ポインターとはまったく異なります。セレクターには、関数ポインターのような呼び出しの完全なコンテキストはありません。
bbum 2009

1
セレクターは関数ポインターと同じではありませんが、それでも似ていると思います。彼らは動詞を表します。C関数ポインターも動詞を表します。追加のコンテキストがないと、どちらも役に立ちません。セレクターにはオブジェクトとパラメーターが必要です。関数ポインターにはパラメーター(操作対象のオブジェクトが含まれる場合があります)が必要です。私のポイントは、必要なすべてのコンテキストを含むNSInvocationオブジェクトとの違いを強調することでした。おそらく私の比較は混乱していたでしょう、その場合私はお詫び申し上げます。
ダニエルヤンコウスキー

1
セレクターは関数ポインターではありません。程遠い。それらは実際には単純なC文字列であり、(「関数」ではなく)メソッドの「名前」が含まれています。それらはパラメータの型を埋め込まないため、メソッドシグネチャでさえありません。オブジェクトは、同じセレクターに対して複数のメソッドを持つことができます(異なるparamタイプ、または異なる戻りタイプ)。
Motti Shneor 2014

-7

2つの間に別の微妙な違いがあります。

    [object doSomething]; // is executed right away

    [object performSelector:@selector(doSomething)]; // gets executed at the next runloop

これはアップルのドキュメントからの抜粋です

"performSelector:withObject:afterDelay:次の実行ループサイクルの間、およびオプションの遅延期間の後、現在のスレッドで指定されたセレクターを実行します。セレクターを実行する次の実行ループサイクルまで待機するため、これらのメソッドは、現在実行中のコード。複数のキューに入れられたセレクターは、キューに入れられた順序で次々に実行されます。」


1
あなたの答えは事実的に間違っています。あなたが引用しているドキュメントはについてperformSelector:withObject:afterDelay:ですが、質問とスニペットはを使用していますがperformSelector:、これはまったく別の方法です。そのドキュメントから:<quote>このperformSelector:方法は、aSelectorメッセージを受信者に直接送信することと同等です。</ quote>
jscs

3
説明についてはJoshに感謝します。あなたは正しいです; 私はperformSelector/performSelector:withObject/performSelector:withObject:afterDelayすべて同じように振る舞ったのは間違いでした。
avi
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.