2011-01-01 12 views
18

これまでに私が学んだことから、Objective-Cでは任意のメッセージを任意のオブジェクトに送信できます。オブジェクトが適切なメソッドを実装している場合は、それ以外の場合は実行されませんが、何も起こりません。これは、メッセージが送信される前にObjective-CがrespondsToSelectorを実行するためです。Objective-C respondsToSelector

私はこれまでのところ願っています。

私は、スライダを動かすたびにアクションが呼び出される場所をテストするための小さなプログラムを作成しました。また、テストのために、送信者をNSButtonに設定しましたが、実際はNSSliderです。今私はsetAlternateTitleに応答するかどうかオブジェクトに尋ねました。 NSButtonは実行しますが、NSSliderは実行しません。私がコードを実行してrespondsToSelector自分でそれはオブジェクトがそのセレクタに応答しないことを教えてくれます。私がのintValueのような何かをテストすると応答します。だから私のコードは今のところうまくいきます。

- (IBAction)sliderDidMove:(id)sender 
{ 
    NSButton *slider = sender; 

    BOOL responds = 
    [slider respondsToSelector:@selector(setAlternateTitle)]; 

    if(responds == YES) 
    { 
     NSLog(@"YES");   
    } 
    else 
    { 
     NSLog(@"NO"); 
    } 

    [slider setAlternateTitle:@"Hello World"]; 
} 

実際にsetAlternateTitleメッセージを送信すると、プログラムがクラッシュすることがあります。その理由はわかりません。メッセージを送信する前にrespondSelectorを実行してはいけませんか?

答えて

146

まず、メソッド名(セレクタ)には、mvdsと同様に、すべてのサブパートとコロン文字が含まれています。

第2に、メソッド-respondsToSelector:は、ランタイムによって呼び出されるのではなく、通常、代理人がプロトコルの任意のメソッドに応答するかどうかを知りたいユーザーまたはAPIによって呼び出されます。

メッセージをオブジェクトに送信すると、ランタイムはオブジェクトのisaポインタを介してオブジェクトのクラス内のメソッドの実装を探します。メッセージ自体はディスパッチされませんが、-respondsToSelector:を送信するのと同じです。メソッドの実装がクラスまたはそのスーパークラスに見つかった場合は、渡されたすべての引数とともに呼び出されます。

実行しない場合、ランタイムはメッセージを2回実行します。これは、オブジェクトのクラスにメッセージ+ (BOOL)resolveInstanceMethod:(SEL)nameを送信することから始まります:このメソッドは、実行時にクラスにメソッドを追加することを可能にします。このメッセージがYESを返す場合、メッセージを再ディスパッチできることを意味します。

メッセージに3番目のチャンスが与えられた場合、セレクタで- (id)forwardingTargetForSelector:(SEL)aSelectorを送信します。このメソッドは、返されたオブジェクトが実際の受信者に代わってセレクタに応答できる別のオブジェクトを返すことができます。メソッドが実行され、元のメッセージによって返されたかのように値が返されます。

返されるオブジェクトがnilまたはself(無限ループを避けるため)の場合、ランタイムはメソッドを実行する4番目のチャンスをメッセージに与えます。呼び出しを構築するためにメッセージ- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelectorにメソッドシグネチャを取得します。提供されている場合、呼び出しはメッセージ- (void)forwardInvocation:(NSInvocation *)anInvocationによって送信されます。このメソッドでは、呼び出しを解析し、必要な方法で他のターゲットに送信するための他のメッセージを作成し、呼び出しの戻り値を設定できます。この値は、元のメッセージの戻り値として機能します。

最後に、オブジェクトによってメソッドシグネチャが返されない場合、ランタイムはメッセージ- (void)doesNotRecognizeSelector:(SEL)aSelectorをオブジェクトに送信します。NSObjectクラスのこのメソッドの実装では例外がスローされます。

+0

+1です。このプロセス全体が詳細に文書化されているリンクをいくつか用意してください。それは読む価値があります。 – taskinoor

+4

docsはここにあります:http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html一部...その他のものはNSObjectクラスリファレンス – Psycho

+1

にあります、forwardingTargetForSelector: 'はMac OS X 10.6+またはiOS 4+にのみあります – user102008

7

一方、selectorは、メッセージの「名前」だけでなく、それに続くもの、つまり引数およびその名前でもあります。

ので、いくつかの-(void)setAlternateTitle:(NSString*)strのための正しい選択は、あなたの問題については

@selector(setAlternateTitle:) 

:

次のようになります。respondsToSelector()クラスならば、あなたはそのセレクタを実行し、あなたが得るべきではありません不明なセレクタを送信するとクラッシュします。どのような種類のクラッシュログがデバッグウィンドウに表示されますか?

(PS。なぜif (responds) { ... }条件ブロックに[slider setAlternateTitle:...]を含んでいない?)

+0

より正確には、セレクタに_ "それに続くもの、つまり引数とその名前" _が含まれず、 "メッセージ名内の引数の数と位置"は含まれません。 –

2

"メッセージの前 対物-Cは respondsToSelectorを実行する送信されるからです"。

これは間違いだと思います。オブジェクトがセレクタに応答しない場合は、実行時にクラッシュします。システムによる自動チェックはありません。ランタイムシステムによるチェックがあった場合、「認識できないセレクタをインスタンスに送る」という例外は決して発生しません。

私が間違っている場合は、私に修正してください。

EDIT:これは単純なクラッシュではありませんが、デフォルトの結果はプロセスが終了することです。全体のシーケンスはすでにコメントや他の答えで説明されているので、私は再びそれを書くつもりはありません。

+2

クラッシュせず、* forwarding *というプロセスに戻ります。残念ながら、これは十分に文書化されているようには見えませんが、いくつかのことがあります。まず、レシーバーのクラスで '+ resolveInstanceMethod:'を呼び出します。これが失敗すると、 '-forwardingTargetForSelector:'、 '-forwardInvocation:'を呼び出します。 '-forwardInvocation:'のデフォルトの実装は実装されていないセレクタ例外をスローします。これはクラッシュと同じではありません。 –

+0

@Ahruman、説明に感謝します。私はこのシーケンスを知らなかった。セグメンテーションフォルトのような実際のストレートフォワードクラッシュではありませんが、デフォルトの結果はプロセスが終了するようです。 – taskinoor

+0

少なくとも、私はこのメソッドがシステムによって自動的に呼び出されないということは間違いありませんでした。この優れた説明は – taskinoor

1

+instancesRespondToSelector:メソッドがあります。名前が示すように、クラスのインスタンスがそのメソッドを実装しているかどうかを示します。

関連する問題