Pourquoi la dernière partie d'un nom de méthode Objective-C doit-elle prendre un argument (quand il y en a plusieurs)?

90

En Objective-C, vous ne pouvez pas déclarer de noms de méthode où le dernier composant ne prend pas d'argument. Par exemple, ce qui suit est illégal.

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

Pourquoi Objective-C a-t-il été conçu de cette façon? Était-ce juste un artefact de Smalltalk dont personne ne voyait le besoin de se débarrasser?

Cette limitation a du sens dans Smalltalk, car Smalltalk n'a pas de délimiteurs autour de l'appel de message, donc le composant final serait interprété comme un message unaire jusqu'au dernier argument. Par exemple, BillyAndBobby take:'$100' andRunserait analysé comme BillyAndBobby take:('$100' andRun). Cela n'a pas d'importance en Objective-C où les crochets sont obligatoires.

La prise en charge des composants de sélecteur sans paramètre ne nous rapporterait pas beaucoup de toutes les manières habituelles de mesurer un langage, comme le nom de méthode choisi par un programmeur (par exemple, runWith:plutôt quetake:andRun) n'affecte pas la sémantique fonctionnelle d'un programme, ni l'expressivité du langage. En effet, un programme avec des composants sans paramètres équivaut à un programme sans. Je ne suis donc pas intéressé par les réponses indiquant qu'une telle fonctionnalité n'est pas nécessaire (à moins que ce ne soit les raisons invoquées par les concepteurs d'Objective-C; est-ce que quelqu'un connaît Brad Cox ou Tom Love? Sont-ils ici?) Ou qui disent comment écrire des noms de méthode pour que la fonctionnalité ne soit pas nécessaire. Le principal avantage est la lisibilité et l'écriture (ce qui est comme la lisibilité, seulement ... vous savez), car cela signifierait que vous pourriez écrire des noms de méthodes qui ressemblent encore plus à des phrases en langage naturel. Les goûts de -(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)theApplication(que Matt Gallagher souligne sur "Cocoa With Love"-(BOOL)application:(NSApplication*)theApplication shouldTerminateAfterLastWindowClosed, plaçant ainsi le paramètre immédiatement à côté du nom approprié.

Le runtime Objective-C d'Apple (par exemple) est parfaitement capable de gérer ce type de sélecteurs, alors pourquoi pas le compilateur? Pourquoi ne pas les soutenir également dans les noms de méthodes?

#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;
}
outis
la source
1
ne peut pas expliquer la raison pour laquelle ce n'est pas possible, mais pourquoi cela devrait-il être possible? Apple nommerait les méthodes "takeAndRun:" et "takeAndDontComplain:";)
Ou takeAndRunWith:(id)theMoneyet takeAndDon'tComplainAbout:(id)yourMedicine. Grammaticalement maladroit, pour être sûr.
Matthew Frederick
13
Merci de poser cette question - c'est une question très intéressante. Je vais demander autour.
bbum
5
C'est la maladresse grammaticale forcée occasionnelle qui me donne envie de cette fonctionnalité.
dehors
1
Excellente question. En passant, le compilateur n'a aucun problème avec les méthodes nommées comme ceci: - (void) :(id)theMoney;ou - (void) :(id)obj1 :(id)obj2;. Les sélecteurs composés uniquement de deux points conviennent donc. ;-)
Ole Begemann

Réponses:

111

C'est Brad Cox. Ma réponse initiale a mal compris la question. J'ai supposé que reallyFast était une extension codée en dur pour déclencher une messagerie plus rapide, pas une sorte de sucre syntaxique. La vraie réponse est que Smalltalk ne l'a pas pris en charge, peut-être parce que son analyseur ne pouvait pas gérer l'ambiguïté (supposée). Bien que les crochets d'OC éliminent toute ambiguïté, je n'ai tout simplement pas pensé à m'éloigner de la structure des mots-clés de Smalltalk.

Brad Cox
la source
Merci, Brad. Mon interprétation de votre réponse était la même; ce départ de SmallTalk n'a pas été envisagé.
bbum
42

21 ans de programmation Objective-C et cette question ne m'a jamais traversé l'esprit. Compte tenu de la conception du langage, le compilateur a raison et les fonctions d'exécution sont fausses ().

La notion d'arguments entrelacés avec des noms de méthode a toujours signifié que, s'il y a au moins un argument, le dernier argument est toujours la dernière partie de la syntaxe d'invocation de méthode.

Sans trop y réfléchir, je parierais qu'il y a des bugs syntaxiques avec la non-application du modèle actuel. Au moins, cela rendrait le compilateur plus difficile à écrire, car toute syntaxe qui a des éléments optionnels entrelacés avec des expressions est toujours plus difficile à analyser. Il pourrait même y avoir un boîtier de bord qui l'empêche complètement. Certes, Obj-C ++ le rendrait plus difficile, mais cela n'a été intégré au langage que des années après que la syntaxe de base ait déjà été gravée dans la pierre.

Quant à savoir pourquoi Objective-C a été conçu de cette façon, je soupçonne que la réponse est que les concepteurs originaux du langage n'ont tout simplement pas envisagé de permettre à la syntaxe entrelacée d'aller au-delà de ce dernier argument.

C'est une meilleure estimation. Je vais demander à l'un d'eux et mettre à jour ma réponse lorsque j'en saurai plus.


J'ai interrogé Brad Cox à ce sujet et il a été très généreux en répondant en détail (Merci, Brad !!):

Je me concentrais à ce moment-là sur la duplication autant de Smalltalk que possible en C et de le faire aussi efficacement que possible. Tous les cycles de rechange allaient à la rapidité de la messagerie ordinaire. Il n'y avait aucune idée d'une option de messagerie spécialisée ("reallyFast?" [ Bbum: j'ai demandé en utilisant 'doSomething: withSomething: reallyFast' comme exemple ]) puisque les messages ordinaires étaient déjà aussi rapides qu'ils pourraient l'être. Cela impliquait de régler manuellement la sortie de l'assembleur du proto-messager C, ce qui était un tel cauchemar de portabilité qu'une partie, sinon la totalité, a été supprimée plus tard. Je me souviens que le messager piraté à la main était très rapide; sur le coût de deux appels de fonction; un pour entrer dans la logique du messager et le reste pour faire des recherches de méthode une fois sur place.
Des améliorations de typage statique ont ensuite été ajoutées en plus du typage dynamique pur de Smalltalk par Steve Naroff et d'autres. Je n'avais qu'une participation limitée à cela.

Allez lire la réponse de Brad!

bbum
la source
Les bugaboos de syntaxe m'intéressent beaucoup.
sortie
1
Il me semble que si vous aviez un morceau de nom de méthode sans deux-points, vous avez un problème d'analyse parce que vous ne pouvez pas être sûr s'il est destiné à faire partie du nom de la méthode ou non.
NSResponder
2
Cela m'est venu à l'esprit la première fois que j'ai conçu un protocole pour un délégué qui était bien moins de 21 ans après avoir commencé avec Objective-C. Le modèle normal consiste à fournir l'objet qui envoie le message du délégué comme premier paramètre. par exemple -myObjectWithDelegate: (id) foo wantsYouToDoSomethingWith: (id) bar. Cela conduit à une incohérence discordante dans les noms de méthode si vous avez une méthode dans le protocole qui n'a besoin d'aucun autre paramètre. Voir NSTableViewDataSource pour un exemple. Une méthode ne suit pas le joli schéma soigné de toutes les autres.
JeremyP
21

Juste pour votre information, le runtime ne se soucie pas réellement des sélecteurs, toute chaîne C est valide, vous pouvez aussi faire un sélecteur comme ça: "== + === + ---__-- ¨¨¨¨ ¨ ^ :::::: "sans argument le runtime l'acceptera, le compilateur ne peut tout simplement pas ou bien il est impossible d'analyser. Il n'y a absolument aucun contrôle de cohérence en ce qui concerne les sélecteurs.

Psycho
la source
7
+1 Je pensais que vous étiez complètement fou de suggérer cela, mais vous avez raison. Pour ceux d'entre vous qui ne croient pas: pastie.org/1388361
Dave DeLong
6

Je suppose qu'ils ne sont pas pris en charge dans Objective-C car ils n'étaient pas non plus disponibles dans Smalltalk. Mais cela a une raison différente de celle que vous pensez: ils ne sont pas nécessaires. Ce qu'il faut, c'est la prise en charge des méthodes avec 0, 1, 2, 3, ... arguments. Pour chaque nombre d'arguments, il existe déjà une syntaxe fonctionnelle pour les appeler. L'ajout de toute autre syntaxe ne ferait que provoquer une confusion inutile.

Si vous vouliez des sélecteurs multi-mots sans paramètre, pourquoi vous arrêter avec un seul mot supplémentaire? On pourrait alors demander que

 [axolotl perform selector: Y with object: Y]

devient également supporté (c'est-à-dire qu'un sélecteur est une séquence de mots, certains avec deux-points et un paramètre, et d'autres non). Bien que cela aurait été possible, je suppose que personne ne l'a jugé utile.

Martin c.Löwis
la source
1
J'avais pensé à l'argument «ils ne sont pas nécessaires», mais cela ne m'intéressait pas. La question mise à jour rend cela plus clair. Permettre aux composants de sélecteurs arbitraires de ne pas prendre de paramètres offre moins d'écriture, car la différence entre les espaces et le cas de chameau est inférieure à la différence entre un composant final sans paramètre et la réécriture des déclarations en langage naturel et des paramètres de repositionnement (les deux doivent être effectués avec ObjC, tel quel).
dehors
De plus, autoriser des composants arbitraires sans paramètres augmente la possibilité de collisions de mots clés et complique la résolution de noms, en particulier lors du mélange de C ++ et d'ObjC ( andet orserait des pierres d'achoppement particulières). Cela se produirait beaucoup moins si seul le composant final d'un nom de méthode était autorisé à ne pas avoir de paramètre, car il aurait tendance à être composé de plusieurs mots.
sortie