Dlaczego ostatnia część nazwy metody Objective-C musi przyjmować argument (jeśli jest więcej niż jedna część)?

90

W Objective-C nie można zadeklarować nazw metod, w których ostatni składnik nie przyjmuje argumentu. Na przykład poniższy kod jest niedozwolony.

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

Dlaczego Objective-C został zaprojektowany w ten sposób? Czy to tylko artefakt Smalltalk, którego nikt nie widział potrzeby się pozbyć?

To ograniczenie ma sens w Smalltalk, ponieważ Smalltalk nie ma ograniczników wokół wywołania wiadomości, więc ostatni składnik byłby interpretowany jako jednoargumentowa wiadomość do ostatniego argumentu. Na przykład BillyAndBobby take:'$100' andRunzostanie przeanalizowany jako BillyAndBobby take:('$100' andRun). Nie ma to znaczenia w Objective-C, gdzie wymagane są nawiasy kwadratowe.

Wspieranie komponentów selektora bez parametrów nie dałoby nam wiele we wszystkich typowych sposobach mierzenia języka, ponieważ nazwa metody, którą wybiera programista (np. runWith:Zamiasttake:andRun) nie wpływa na semantykę funkcjonalną programu ani na ekspresję języka. Rzeczywiście, program z komponentami bez parametrów jest alfa równoważny programowi bez. Dlatego nie interesują mnie odpowiedzi, które stwierdzają, że taka funkcja nie jest konieczna (chyba że takie były powody projektantów Objective-C; czy ktoś zdarzyło się znać Brada Coxa lub Toma Love? Czy oni tu są?) Lub które mówią jak pisać nazwy metod, aby funkcja nie była potrzebna. Podstawową zaletą jest czytelność i zapisywalność (co jest jak czytelność, tylko ... wiesz), ponieważ oznaczałoby to, że możesz pisać nazwy metod, które jeszcze bardziej przypominają zdania w języku naturalnym. Takie jak -(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)theApplication(na co Matt Gallagher zwraca uwagę w „Cocoa With Love”-(BOOL)application:(NSApplication*)theApplication shouldTerminateAfterLastWindowClosed, umieszczając w ten sposób parametr bezpośrednio obok odpowiedniego rzeczownika.

Środowisko uruchomieniowe Objective-C firmy Apple (na przykład) doskonale radzi sobie z tego rodzaju selektorami, więc dlaczego nie kompilator? Dlaczego nie wspierać ich również w nazwach metod?

#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;
}
poza
źródło
1
nie potrafię wyjaśnić, dlaczego nie jest to możliwe, ale dlaczego miałoby to być możliwe? Apple nazwałby metody „takeAndRun:” i „takeAndDontComplain:”;)
Lub takeAndRunWith:(id)theMoneyi takeAndDon'tComplainAbout:(id)yourMedicine. Z pewnością gramatycznie niezręczny.
Matthew Frederick
13
Dzięki, że o to zadałeś - to bardzo interesujące pytanie. Zapytam dookoła.
bbum
5
To sporadyczna wymuszona gramatyczna niezręczność sprawia, że ​​chcę tę funkcję.
wyjazd
1
Świetne pytanie. Tak na marginesie, kompilator nie ma problemu z metodami nazwanymi w ten sposób: - (void) :(id)theMoney;lub - (void) :(id)obj1 :(id)obj2;. Tak więc selektory składające się wyłącznie z dwukropków są w porządku. ;-)
Ole Begemann

Odpowiedzi:

111

To jest Brad Cox. Moja pierwotna odpowiedź źle zrozumiała pytanie. Założyłem, że naprawdę Fast to zakodowane na stałe rozszerzenie uruchamiające szybsze przesyłanie wiadomości, a nie rodzaj cukru syntaktycznego. Prawdziwą odpowiedzią jest to, że Smalltalk go nie obsługiwał, być może dlatego, że jego parser nie radził sobie z (zakładaną) niejednoznacznością. Chociaż nawiasy kwadratowe w OC usunęłyby wszelkie niejasności, po prostu nie pomyślałem o odejściu od struktury słów kluczowych Smalltalka.

Brad Cox
źródło
Dzięki, Brad. Moja interpretacja twojej odpowiedzi była taka sama; to odejście od SmallTalk nie zostało uwzględnione.
bbum
42

21 lat programowania Objective-C i to pytanie nigdy nie przyszło mi do głowy. Biorąc pod uwagę projekt języka, kompilator ma rację, a funkcje uruchomieniowe są nieprawidłowe ().

Pojęcie przeplatanych argumentów z nazwami metod zawsze oznaczało, że jeśli istnieje co najmniej jeden argument, ostatni argument jest zawsze ostatnią częścią składni wywołania metody.

Bez zastanawiania się nad tym strasznie dużo, założę się, że są pewne błędy składniowe, w których brak narzucania obecnego wzorca. Przynajmniej utrudniłoby to pisanie kompilatorowi, ponieważ każda składnia, która ma elementy opcjonalne przeplecione wyrażeniami, jest zawsze trudniejsza do przeanalizowania. Może nawet istnieć przypadek krawędzi, który płasko temu zapobiega. Z pewnością Obj-C ++ uczyniłby to trudniejszym, ale nie zostało to zintegrowane z językiem aż do lat po tym, jak podstawowa składnia została już ustalona.

Jeśli chodzi o powód, dla którego Objective-C został zaprojektowany w ten sposób, podejrzewam, że odpowiedź brzmi, że pierwotni projektanci języka po prostu nie rozważali dopuszczenia przeplatanej składni do wyjścia poza ten ostatni argument.

To najlepsze przypuszczenie. Zapytam jednego z nich i zaktualizuję odpowiedź, gdy dowiem się więcej.


Zapytałem o to Brada Coxa i był bardzo hojny w szczegółowej odpowiedzi (Dzięki, Brad !!):

W tym czasie byłem skupiony na powielaniu jak największej ilości Smalltalk w C i robieniu tego tak wydajnie, jak to tylko możliwe. Wszelkie wolne cykle wymagały szybkiego wysyłania zwykłych wiadomości. Nie myśleliśmy o wyspecjalizowanej opcji przesyłania wiadomości („NaprawdęSzybki?” [ Bbum: Zapytałem, używając na przykładzie „doSomething: withSomething: reallyFast” ]), ponieważ zwykłe wiadomości były już tak szybkie, jak tylko mogły. Wymagało to ręcznego dostrajania wyjścia asemblera protokołu C proto-messager, co było takim koszmarem przenośności, że część, jeśli nie całość, została później usunięta. Pamiętam, że ręcznie zhakowany komunikator był bardzo szybki; o koszcie dwóch wywołań funkcji; jeden, aby dostać się do logiki komunikatora, a reszta do wyszukiwania metod.
Ulepszenia pisania statycznego zostały później dodane do czystego dynamicznego pisania Smalltalk przez Steve'a Naroffa i innych. Miałem w to ograniczony udział.

Przeczytaj odpowiedź Brada!

bbum
źródło
Bardzo mnie interesuje składnia bugaboos.
outis
1
Wydaje mi się, że jeśli masz fragment nazwy metody bez dwukropka, masz problem z analizą, ponieważ nie możesz być pewien, czy ma być częścią nazwy metody, czy nie.
NSResponder
2
Przyszło mi do głowy, kiedy po raz pierwszy zaprojektowałem protokół dla delegata, który był znacznie mniej niż 21 lat po rozpoczęciu pracy z Objective-C. Normalnym wzorcem jest dostarczenie obiektu wysyłającego wiadomość delegata jako pierwszy parametr. np -myObjectWithDelegate: (id) foo wantsYouToDoSomethingWith: (id) bar. Prowadzi to do irytującej niespójności w nazwach metod, jeśli w protokole jest metoda, która nie wymaga innych parametrów. Aby zapoznać się z przykładem, zobacz NSTableViewDataSource. Jedna metoda nie jest zgodna z ładnym, schludnym wzorcem wszystkich innych.
JeremyP
21

Tak dla twojej informacji, środowisko wykonawcze tak naprawdę nie dba o selektory, każdy łańcuch C jest prawidłowy, równie dobrze możesz stworzyć taki selektor: "== + === + ---__-- ¨¨¨¨ ¨ ^ :::::: "bez argumentu środowisko wykonawcze zaakceptuje to, kompilator po prostu nie może, albo nie można go przeanalizować. W przypadku selektorów nie ma absolutnie żadnej kontroli poczytalności.

Psycho
źródło
7
+1 Myślałem, że sugerując to, zwariowałeś, ale masz rację. Dla tych z Was, którzy nie wierzą: pastie.org/1388361
Dave DeLong
6

Zakładam, że nie są obsługiwane w Objective-C, ponieważ nie były również dostępne w Smalltalk. Ale ma to inny powód niż myślisz: nie są potrzebne. Potrzebna jest obsługa metod z argumentami 0, 1, 2, 3, .... Dla każdej liczby argumentów istnieje już działająca składnia do ich wywoływania. Dodanie jakiejkolwiek innej składni po prostu spowodowałoby niepotrzebne zamieszanie.

Jeśli potrzebujesz selektorów wielowyrazowych bez parametrów, po co poprzestać na jednym dodatkowym słowie? Można by wtedy o to zapytać

 [axolotl perform selector: Y with object: Y]

również staje się obsługiwany (tj. że selektor jest sekwencją słów, niektóre z dwukropkiem i parametrem, a inne nie). Chociaż byłoby to możliwe, zakładam, że nikt nie uznał tego za warte zachodu.

Martin przeciwko Löwis
źródło
1
Myślałem o argumencie „oni nie są potrzebni”, ale nie byłem nim zainteresowany. Zaktualizowane pytanie czyni to jaśniejszym. Umożliwienie arbitralnym komponentom selektora nieprzyjmowania parametrów daje mniejszą możliwość zapisywania, ponieważ różnica między spacjami a wielkością wielbłąda jest mniejsza niż różnica między końcowym składnikiem bez parametrów a przepisywaniem instrukcji języka naturalnego i parametrami zmiany położenia (oba te elementy należy wykonać z ObjC, jak jest).
outis
Ponadto zezwolenie na dowolne komponenty bez parametrów zwiększa możliwość kolizji słów kluczowych i komplikuje rozwiązywanie nazw, szczególnie podczas mieszania C ++ i ObjC ( andi orbyłoby to szczególnymi przeszkodami). Byłoby to znacznie rzadziej, gdyby tylko ostatni składnik nazwy metody nie miał parametru, ponieważ składałby się z wielu słów.
wyjazd