Objective-Cメソッド名の最後の部分に引数が必要なのはなぜですか(複数の部分がある場合)?


90

Objective-Cでは、最後のコンポーネントが引数を取らない場所でメソッド名を宣言することはできません。たとえば、次は違法です。

-(void)take:(id)theMoney andRun;
-(void)take:(id)yourMedicine andDontComplain;

なぜObjective-Cはこのように設計されたのですか?それは、Smalltalkのアーティファクトだけでしたか?

Smalltalkではメッセージの呼び出しに区切り文字がないため、この制限はSmalltalkでは理にかなっています。したがって、最後のコンポーネントは最後の引数に対する単項メッセージとして解釈されます。たとえば、BillyAndBobby take:'$100' andRunはとして解析されBillyAndBobby take:('$100' andRun)ます。これは、角かっこが必要なObjective-Cでは問題になりません。

パラメータなしのセレクタコンポーネントをサポートしても、プログラマが選択するメソッド名(たとえばrunWith:take:andRun)は、プログラムの機能的なセマンティクスにも、言語の表現力にも影響しません。確かに、パラメーターのないコンポーネントを含むプログラムは、アルファを持たないものと同じです。したがって、そのような機能が不要であると述べている回答(Objective-Cデザイナーの理由が記載されている場合を除き、誰かがたまたまBrad CoxやTom Loveを知っていますか?彼らはここにいますか?)には興味がありません。メソッド名を記述して、機能が不要になるようにする方法。主な利点は、可読性と書き込み可能性(読みやすさのようなものですが、ご存知のとおりです)です。これは、自然言語の文にさらに似たメソッド名を記述できることを意味します。同類-(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)theApplicationマット・ギャラガーが指摘する(「ココアと恋」に-(BOOL)application:(NSApplication*)theApplication shouldTerminateAfterLastWindowClosed、したがって、パラメータを適切な名詞のすぐ隣に配置します。

AppleのObjective-Cランタイム(たとえば)は、この種のセレクターを完全に処理できるので、コンパイラーはどうしてでしょうか?それらをメソッド名でもサポートしないのはなぜですか?

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@interface Potrzebie : NSObject
-(void)take:(id)thing;
@end

@implementation Potrzebie
+(void)initialize {
    SEL take_andRun = NSSelectorFromString(@"take:andRun");
    IMP take_ = class_getMethodImplementation(self, @selector(take:));
    if (take_) {
        if (NO == class_addMethod(self, take_andRun, take_, "@@:@")) {
            NSLog(@"Couldn't add selector '%@' to class %s.", 
                  NSStringFromSelector(take_andRun), 
                  class_getName(self));
        }
    } else {
        NSLog(@"Couldn't find method 'take:'.");
    }
}

-(void)take:(id)thing {
    NSLog(@"-take: (actually %@) %@",NSStringFromSelector(_cmd), thing);
}
@end

int main() {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    Potrzebie *axolotl=[[Potrzebie alloc] init];
    [axolotl take:@"paradichloroaminobenzaldehyde"];
    [axolotl performSelector:NSSelectorFromString(@"take:andRun") 
                  withObject:@"$100"];
    [axolotl release];

    [pool release];
    return 0;
}

1
それが不可能である理由を説明することはできませんが、なぜこれが可能であるべきなのでしょうか?Appleはメソッドに「takeAndRun:」および「takeAndDontComplain:」という名前を付けます;)

またはtakeAndRunWith:(id)theMoneyおよびtakeAndDon'tComplainAbout:(id)yourMedicine。確かに、文法的に厄介です。
マシューフレデリック

13
お問い合わせいただきありがとうございます。非常に興味深い質問です。周りを尋ねます。
bbum

5
この機能が必要になるのは、文法上不自然になってしまうことがあるからです。
10

1
すばらしい質問です。余談ですが、コンパイラは次のような名前のメソッドでも問題ありません:- (void) :(id)theMoney;または- (void) :(id)obj1 :(id)obj2;。したがって、コロンのみで構成されるセレクターは問題ありません。;-)
Ole Begemann 2010

回答:


111

ブラッド・コックスです。私の最初の答えはその質問を誤解していた。私は、reallyFastは、高速なメッセージングをトリガーするハードコードされた拡張機能であり、一種の構文上の砂糖ではないと想定しました。本当の答えは、Smalltalkがそれをサポートしなかったということです。おそらく、そのパーサーが(想定された)あいまいさを処理できなかったためでしょう。OCの角かっこはあいまいさを取り除きますが、Smalltalkのキーワード構造から逸脱することは考えていませんでした。


ありがとう、ブラッド。あなたの答えの私の解釈は同じでした。SmallTalkからの離脱は考慮されていません。
bbum

42

Objective-Cのプログラミングの21年とこの質問は、私の心を超えたことはありません。言語設計を考えると、コンパイラは正しく、ランタイム関数は間違っています()。

インターリーブされた引数とメソッド名の概念は、少なくとも1つの引数がある場合、最後の引数が常にメソッド呼び出し構文の最後の部分であることを常に意味しています。

ひどく熟考せずに、現在のパターンを強制しない構文のブガブーがあると思います。少なくとも、式でインターリーブされたオプションの要素を持つ構文は常に解析が難しいので、コンパイラーを書くこと難しくなります。平らな状態がそれを妨げるエッジケースがあるかもしれません。確かに、Obj-C ++はそれをより困難にしますが、基本構文がすでに確定されてから何年か経つまで、それは言語に統合されませんでした。

Objective-Cがこのように設計された理由については、言語の元の設計者が、インターリーブされた構文がその最後の引数を超えることを許可することを考慮していなかったためだと思います。

それは最良の推測です。そのうちの1つに質問し、詳細がわかったら回答を更新します。


私はこれについてブラッド・コックスに尋ねました、そして彼は詳細に応答するのにとても寛大でした(ありがとう、ブラッド!!):

私は当時、Cで可能な限り多くのSmalltalkを複製し、それを可能な限り効率的に行うことに集中していました。通常のメッセージングを高速化するために、余分なサイクルが発生しました。特殊なメッセージングオプション( "reallyFast?" [ bbum:例として 'doSomething:withSomething:reallyFast'を例として使用するように頼んだ ])については考えられませんでした。これには、Cプロトメッセンジャーのアセンブラー出力を手動で調整する必要がありました。手動でハッキングされたメッセージが非常に高速だったことを思い出します。2つの関数呼び出しのコストについて。1つはメッセージャロジックに入り、残りはそこでメソッド検索を実行するためのものです。
静的型付けの拡張機能は、スティーブナロフなどによるSmalltalkの純粋な動的型付けの上に追加されました。私はそれに限られた関与しかありませんでした。

ブラッドの答えを読んでください!


構文のバグアボは私に非常に興味があります。
10

1
コロンのないメソッド名のチャンクがある場合、それがメソッド名の一部であることが意図されているかどうかわからないため、解析の問題が発生しているようです。
NSResponder 2010

2
Objective-Cを使い始めてから21年も経っていないデリゲートのプロトコルを初めて設計したとき、それは私の心を超えました。通常のパターンでは、デリゲートメッセージを送信するオブジェクトを最初のパラメーターとして指定します。例えば-myObjectWithDelegate: (id) foo wantsYouToDoSomethingWith: (id) bar。これにより、他のパラメータを必要としないプロトコルのメソッドがある場合、メソッド名に不整合が生じます。例については、NSTableViewDataSourceを参照してください。1つの方法は、他のすべての方法の素敵なきちんとしたパターンに従っていません。
JeremyP

21

参考までに、ランタイムは実際にはセレクターを気にしません。C文字列はすべて有効です。次のようなセレクターを作成することもできます。 "== + === + ---__--¨¨¨¨ ¨^ :::::: "引数がなければ、ランタイムはそれを受け入れますが、コンパイラはそれができないか、そうでなければ解析できません。セレクタに関しては、完全性チェックは絶対にありません。


7
+1あなたはこれを提案することに全く夢中になっていると思いましたが、あなたは正しいです。信じない人のために:pastie.org/1388361
Dave DeLong

6

Smalltalkでも利用できなかったため、Objective-Cではサポートされていないと思います。しかし、それはあなたが考えるのとは異なる理由があります。それらは必要ではありません。必要なのは、引数が0、1、2、3、...のメソッドのサポートです。引数の数ごとに、それらを呼び出すための構文がすでに機能しています。他の構文を追加すると、不要な混乱が生じるだけです。

マルチワードのパラメーターレスセレクターが必要な場合、なぜ1つの追加のワードで停止するのですか?次に、それを尋ねるかもしれません

 [axolotl perform selector: Y with object: Y]

また、サポートされるようになりました(つまり、セレクターは単語のシーケンスであり、コロンとパラメーターを持つものもあれば、そうでないものもあります)。これは可能だっただろうが、私はだれもそれを価値があると考えなかったと思います。


1
「必要ない」と思っていたのですが、興味がありませんでした。更新された質問は、これをより明確にします。スペースとキャメルケースの違いは、最終的なパラメータのないコンポーネントと自然言語ステートメントの書き換えとパラメータの再配置(どちらも実行する必要がある)の違いよりも小さいため、任意のセレクタコンポーネントがパラメータを取らないようにすると、書き込み可能性が低下します。そのままObjCで)。
10

また、任意のパラメータのないコンポーネントを許可するとCを混合++とにObjC(特にとき、キーワードの衝突の可能性を提起し、名前解決を複雑にandし、or特定のネックブロックであろう)。メソッド名の最後のコンポーネントのみがパラメーターを持たないようにすると、複数の単語で構成される傾向があるため、これははるかに少なくなります。
10
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.