Pourquoi Objective-C ne prend-il pas en charge les méthodes privées?

123

J'ai vu un certain nombre de stratégies pour déclarer des méthodes semi-privées en Objective-C , mais il ne semble pas y avoir de moyen de créer une méthode vraiment privée. Je reconnais que. Mais pourquoi est-ce ainsi? Chaque explication que j'ai dit essentiellement, "vous ne pouvez pas le faire, mais voici une approximation proche."

Il y a un certain nombre de mots - clés appliqués aux ivars(membres) qui contrôlent leur champ d' application, par exemple @private, @public, @protected. Pourquoi cela ne peut-il pas être fait pour les méthodes également? Il semble que le runtime devrait pouvoir prendre en charge. Y a-t-il une philosophie sous-jacente qui me manque? Est-ce délibéré?

Rob Jones
la source
15
Bonne question, btw.
bbum
5
À vos modifications: oui, l'application d'exécution serait très coûteuse pour très peu d'avantages. L'application au moment de la compilation serait un ajout bienvenu au langage et est tout à fait réalisable. Oui, cela pourrait être contourné lors de l'exécution, mais c'est très bien. Le programmeur n'est pas un ennemi. Le but de la portée est d'aider à protéger le programmeur des erreurs.
Rob Napier
1
Vous ne pouvez pas "ignorer le runtime" - c'est le runtime qui distribue les messages.
Chuck
"Il n'y aurait aucun moyen pour une méthode privée de court-circuiter ce contrôle" Voulez-vous dire "contourner"? ;-)
Constantino Tsarouhas

Réponses:

103

La réponse est ... eh bien ... simple. Simplicité et cohérence, en fait.

Objective-C est purement dynamique au moment de l'envoi de la méthode. En particulier, chaque envoi de méthode passe par le même point de résolution de méthode dynamique que chaque autre envoi de méthode. Au moment de l'exécution, chaque implémentation de méthode a exactement la même exposition et toutes les API fournies par le runtime Objective-C qui fonctionnent avec des méthodes et des sélecteurs fonctionnent de la même manière dans toutes les méthodes.

Comme beaucoup l'ont répondu (ici et dans d'autres questions), les méthodes privées à la compilation sont supportées; si une classe ne déclare pas une méthode dans son interface accessible au public, alors cette méthode pourrait tout aussi bien ne pas exister en ce qui concerne votre code. En d'autres termes, vous pouvez obtenir toutes les différentes combinaisons de visibilité souhaitées au moment de la compilation en organisant votre projet de manière appropriée.

Il y a peu d'avantages à dupliquer la même fonctionnalité dans le runtime. Cela ajouterait énormément de complexité et de frais généraux. Et même avec toute cette complexité, cela n'empêcherait toujours pas tous les développeurs, sauf les plus occasionnels, d'exécuter vos méthodes supposées "privées".

EDIT: L'une des hypothèses que j'ai remarquées est que les messages privés devraient passer par l'exécution, ce qui entraînerait une surcharge potentiellement importante. Est-ce absolument vrai?

Oui, ça l'est. Il n'y a aucune raison de supposer que l'implémenteur d'une classe ne souhaite pas utiliser toutes les fonctionnalités d'Objective-C dans l'implémentation, et cela signifie que la répartition dynamique doit avoir lieu. Cependant , il n'y a aucune raison particulière pour laquelle les méthodes privées ne peuvent pas être distribuées par une variante spéciale de objc_msgSend(), puisque le compilateur saurait qu'elles sont privées; c'est-à-dire que cela pourrait être réalisé en ajoutant une table de méthode uniquement privée à la Classstructure.

Il n'y aurait aucun moyen pour une méthode privée de court-circuiter cette vérification ou de sauter le runtime?

Il ne pouvait pas ignorer le runtime, mais le runtime n'aurait pas nécessairement à vérifier les méthodes privées.

Cela dit, il n'y a aucune raison qu'un tiers ne puisse pas appeler délibérément objc_msgSendPrivate() un objet, en dehors de la mise en œuvre de cet objet, et certaines choses (KVO, par exemple) devraient le faire. En effet, ce serait juste une convention et guère mieux en pratique que de préfixer les sélecteurs de méthodes privées ou de ne pas les mentionner dans l'en-tête de l'interface.

Faire ainsi, cependant, minerait la nature dynamique pure de la langue. Chaque envoi de méthode ne passerait plus par un mécanisme d'envoi identique. Au lieu de cela, vous vous retrouveriez dans une situation où la plupart des méthodes se comportent d'une manière et une petite poignée est simplement différente.

Cela va au-delà du runtime car il existe de nombreux mécanismes dans Cocoa basés sur le dynamisme cohérent d'Objective-C. Par exemple, le codage des valeurs clés et l'observation des valeurs clés devraient soit être très fortement modifiés pour prendre en charge les méthodes privées - très probablement en créant une faille exploitable - ou les méthodes privées seraient incompatibles.

bbum
la source
49
+1 Objective-C est (à certains égards) un langage "big-boy" dans lequel les programmeurs sont censés faire preuve d'une certaine discipline, plutôt que d'être giflés par le compilateur / runtime à chaque tour. Je trouve ça rafraîchissant. Pour moi, restreindre la visibilité des méthodes lors de la compilation / liaison est suffisant et préférable.
Quinn Taylor
11
Plus de python étant "obj-c-ic" :). Guido a été assez proactif dans la maintenance de Python sur les systèmes NeXT, notamment en créant la 1ère version de PyObjC. Ainsi, ObjC a quelque peu influencé python .
bbum
3
+1 pour l'histoire intéressante du dictateur bienveillant pour la vie et ses croisements avec le mythique système NeXT.
pokstad du
5
Je vous remercie. Python, Java et Objective-C ont tous des modèles d'objet d'exécution qui sont assez similaires [à SmallTalk]. Java était assez directement dérivé d'Objective-C. Python a mené un cours parallèle plus qu'un d'inspiration, mais Guido a certainement étudié NeXTSTEP de près.
bbum
1
Sauf que je ne fais pas confiance à sa licence et que je préférerais définitivement clang à gcc. Mais c'est un bon premier pas à coup sûr.
Cthutu
18

Le runtime pourrait le supporter mais le coût serait énorme. Chaque sélecteur envoyé devrait être vérifié pour savoir s'il est privé ou public pour cette classe, ou chaque classe devrait gérer deux tables de répartition distinctes. Ce n'est pas la même chose pour les variables d'instance car ce niveau de protection est effectué au moment de la compilation.

En outre, le moteur d'exécution doit vérifier que l'expéditeur d'un message privé est de la même classe que le destinataire. Vous pouvez également contourner les méthodes privées; si la classe utilisée instanceMethodForSelector:, elle pourrait donner le retourIMP à n'importe quelle autre classe pour qu'ils invoquent directement la méthode privée.

Les méthodes privées n'ont pas pu contourner l'envoi du message. Considérez le scénario suivant:

  1. Une classe AllPublica une méthode d'instance publiquedoSomething

  2. Une autre classe HasPrivatea une méthode d'instance privée également appeléedoSomething

  3. Vous créez un tableau contenant n'importe quel nombre d'instances des deux AllPublicetHasPrivate

  4. Vous avez la boucle suivante:

    for (id anObject in myArray)
        [anObject doSomething];

    Si vous exécutiez cette boucle de l'intérieur AllPublic, le runtime devrait vous empêcher d'envoyer doSomethingsur les HasPrivateinstances, mais cette boucle serait utilisable si elle était à l'intérieur de la HasPrivateclasse.

dreamlax
la source
1
Je conviens qu'il y aurait un coût. Ce n'est peut-être pas quelque chose que vous voulez sur un appareil portable. Pouvez-vous soutenir l'énorme qualificatif autrement? Le coût aurait-il vraiment une importance sur un système de bureau?
Rob Jones
1
Je n'ai pas pensé à cela, mais vous avez peut-être raison. Objective-C a une répartition des messages d'exécution par rapport à l'appel de la méthode de compilation de C ++, vous parleriez donc d'une vérification de la compilation par rapport à une vérification de l'exécution.
Remus Rusanu
Il semble vraiment étrange que la principale raison de ne pas prendre en charge les méthodes privées soit due aux performances. Je pense qu'Apple ne pensait tout simplement pas que les méthodes privées étaient très nécessaires, qu'il y ait ou non un impact négatif sur les performances. Sinon, ils auraient dû choisir une langue différente pour leur entreprise de plusieurs milliards de dollars.
pokstad
1
L'ajout de méthodes privées ne compliquerait pas seulement l'implémentation du runtime, cela compliquerait la sémantique du langage. Le contrôle d'accès est un concept simple dans les langages statiques, mais il ne correspond pas à la répartition dynamique qui entraînerait beaucoup de surcharge conceptuelle et conduirait à de très nombreux "pourquoi cela ne fonctionne-t-il pas?" des questions.
Jens Ayton le
7
Qu'est-ce que les méthodes privées vous achèteraient vraiment? Objective-C est, après tout, un langage basé sur C. Ainsi, si quelqu'un a du code en cours d'exécution dans votre processus, il peut facilement p0wnz votre processus quel que soit le degré de confidentialité ou d'obscurcissement d'une API.
bbum
13

Les réponses publiées jusqu'à présent permettent de bien répondre à la question d'un point de vue philosophique, je vais donc poser une raison plus pragmatique: que gagnerait-il à changer la sémantique du langage? C'est assez simple pour "cacher" efficacement les méthodes privées. À titre d'exemple, imaginez que vous ayez une classe déclarée dans un fichier d'en-tête, comme ceci:

@interface MyObject : NSObject {}
- (void) doSomething;
@end

Si vous avez besoin de méthodes "privées", vous pouvez également les mettre dans le fichier d'implémentation:

@interface MyObject (Private)
- (void) doSomeHelperThing;
@end

@implementation MyObject

- (void) doSomething
{
    // Do some stuff
    [self doSomeHelperThing];
    // Do some other stuff;
}

- (void) doSomeHelperThing
{
    // Do some helper stuff
}

@end

Bien sûr, ce n'est pas tout à fait la même chose que les méthodes privées C ++ / Java, mais c'est en fait assez proche, alors pourquoi modifier la sémantique du langage, ainsi que le compilateur, le runtime, etc., pour ajouter une fonctionnalité déjà émulée dans un format acceptable façon? Comme indiqué dans d'autres réponses, la sémantique de passage des messages - et leur dépendance à la réflexion d'exécution - rendrait la gestion des messages "privés" non triviale.

mipadi
la source
1
Le problème avec cette approche est que quelqu'un peut remplacer les méthodes privées (même sans le savoir) quand ils sous-classent votre classe. Je suppose que vous devez essentiellement choisir des noms qui ne seront probablement pas remplacés.
dreamlax
3
Ce qui ressemble à un hack en plus d'un hack pour moi.
Rob Jones
1
@Mike: Apple a déclaré que les noms de méthodes avec des traits de soulignement en tête sont explicitement réservés à Apple uniquement: developer.apple.com/mac/library/documentation/Cocoa/Conceptual/… . L'alternative recommandée est de préfixer avec deux lettres majuscules, puis un trait de soulignement.
dreamlax
8
Ajoutez également votre SSN après la méthode pour que les autres programmeurs sachent qui l'a écrite. = D Espaces de noms, en avez besoin !!!
pokstad
2
Si vous craignez que les méthodes que vous définissez ne soient remplacées, vous n'avez pas correctement adopté les niveaux de verbosité inhérents qu'Objective-C encourage ...
Kendall Helmstetter Gelner
7

La solution la plus simple consiste simplement à déclarer certaines fonctions C statiques dans vos classes Objective-C. Celles-ci ont uniquement une portée de fichier selon les règles C pour le mot clé static et à cause de cela, elles ne peuvent être utilisées que par les méthodes de cette classe.

Pas de problème du tout.

Cromulent
la source
Ces derniers doivent-ils être définis en dehors de la section @implementation?
Rob Jones
@Rob - non, ils peuvent (et devraient probablement l'être) définis dans l'implémentation de vos classes.
Cromulent
6

Oui, cela peut être fait sans affecter le runtime en utilisant une technique déjà employée par le (s) compilateur (s) pour gérer C ++: la gestion des noms.

Cela n'a pas été fait parce qu'il n'a pas été établi que cela résoudrait une difficulté considérable dans l'espace des problèmes de codage que d'autres techniques (par exemple, le préfixe ou le soulignement) sont capables de contourner suffisamment. IOW, vous avez besoin de plus de douleur pour surmonter les habitudes enracinées.

Vous pouvez apporter des correctifs à clang ou gcc qui ajoutent des méthodes privées à la syntaxe et génèrent des noms mutilés qu'il a seuls reconnus lors de la compilation (et oubliés rapidement). Ensuite, d'autres membres de la communauté Objective-C seraient en mesure de déterminer si cela en valait la peine ou non. Cela sera probablement plus rapide que d'essayer de convaincre les développeurs.

Huperniketes
la source
2
La modification des noms au moment de la compilation est beaucoup plus difficile que ce que l'on pourrait supposer car vous devez modifier tous les sélecteurs de méthode, y compris ceux qui peuvent apparaître dans les fichiers XIB et ceux qui pourraient être passés à des éléments tels que NSSelectorFromString () ainsi que KeyValueCoding & friends ...
bbum
1
Oui, ce serait plus compliqué si vous vouliez fournir un support pour les méthodes privées à travers la chaîne d'outils. La table des symboles du compilateur devrait être stockée et accessible via une API de chaîne d'outils. Ce ne serait pas la première fois qu'Apple devait déployer progressivement des capacités aux développeurs qui étaient suffisamment importantes pour le temps et la stabilité. Cependant, en ce qui concerne les méthodes privées: il est douteux que ce soit une victoire suffisante pour entreprendre l'effort. Il est encore plus suspect qu'ils devraient être accessibles en dehors de la classe elle-même par ces autres mécanismes.
Huperniketes
1
Comment une application et une modification du nom ne peuvent-elles empêcher quelqu'un de parcourir la liste de méthodes d'une classe et de la trouver?
dreamlax
Ça ne le ferait pas. Ma réponse ne répond qu'à la question posée par le PO.
Huperniketes
J'ai pensé essayer d'ajouter des méthodes privées à Clang, une des raisons pour lesquelles je pensais que les méthodes privées seraient bonnes est qu'elles pourraient être appelées directement comme de simples fonctions c et que toutes les optimisations habituelles de la fonction C pourraient alors se produire. Je n'ai jamais dérangé à cause du temps et vous pouvez déjà le faire en utilisant simplement c.
Nathan Day
4

Essentiellement, cela a à voir avec la forme d'appels de méthode de transmission de messages d'Objective-C. N'importe quel message peut être envoyé à n'importe quel objet, et l'objet choisit comment répondre au message. Normalement, il répondra en exécutant la méthode nommée d'après le message, mais il pourrait également répondre de plusieurs autres manières. Cela ne rend pas les méthodes privées complètement impossibles - Ruby le fait avec un système de transmission de messages similaire - mais cela les rend quelque peu gênantes.

Même l'implémentation de méthodes privées par Ruby est un peu déroutante pour les gens à cause de l'étrangeté (vous pouvez envoyer à l'objet n'importe quel message que vous aimez, sauf ceux de cette liste !). Essentiellement, Ruby le fait fonctionner en interdisant aux méthodes privées d'être appelées avec un récepteur explicite. En Objective-C, cela nécessiterait encore plus de travail car Objective-C n'a pas cette option.

Mandrin
la source
1

C'est un problème avec l'environnement d'exécution d'Objective-C. Alors que C / C ++ se compile en code machine illisible, Objective-C conserve toujours certains attributs lisibles par l'homme comme les noms de méthodes sous forme de chaînes . Cela donne à Objective-C la possibilité d'exécuter des fonctions réfléchissantes .

EDIT: Être un langage réflexif sans méthodes privées strictes rend Objective-C plus "pythonique" en ce sens que vous faites confiance aux autres personnes qui utilisent votre code plutôt que de restreindre les méthodes qu'elles peuvent appeler. L'utilisation de conventions de dénomination telles que les doubles traits de soulignement vise à masquer votre code à un codeur client occasionnel, mais n'empêchera pas les codeurs de faire un travail plus sérieux.

Pokstad
la source
Et si les consommateurs de votre bibliothèque pouvaient refléter vos méthodes privées, ne pourraient-ils pas les appeler aussi?
Jared Updike
S'ils savent où chercher, n'est-ce pas ainsi que les gens utilisent des bibliothèques non documentées sur l'iPhone? Le problème avec l'iPhone est que vous risquez de ne pas avoir votre application acceptée pour l'utilisation d'une API privée.
pokstad
S'ils accèdent à des méthodes privées par réflexion, ils obtiennent ce qu'ils méritent. Les erreurs ne sont pas de votre faute.
gnasher729
1

Il y a deux réponses selon l'interprétation de la question.

La première consiste à masquer l'implémentation de la méthode de l'interface. Ceci est utilisé, généralement avec une catégorie sans nom (par exemple@interface Foo() ). Cela permet à l'objet d'envoyer ces messages mais pas d'autres - bien que l'on puisse toujours les remplacer accidentellement (ou autrement).

La deuxième réponse, en supposant qu'il s'agit de performances et d'inlining, est rendue possible mais en tant que fonction C locale à la place. Si vous vouliez une NSString *argméthode 'private foo ( )', vous la feriez void MyClass_foo(MyClass *self, NSString *arg)et l'appeliez comme une fonction C comme MyClass_foo(self,arg). La syntaxe est différente, mais elle agit avec le type sain de caractéristiques de performance des méthodes privées de C ++.

Bien que cela réponde à la question, je dois souligner que la catégorie sans nom est de loin la manière la plus courante d'Objective-C de le faire.

AlBlue
la source
0

Objective-C ne prend pas en charge les méthodes privées car il n'en a pas besoin.

En C ++, chaque méthode doit être visible dans la déclaration de la classe. Vous ne pouvez pas avoir de méthodes que quelqu'un incluant le fichier d'en-tête ne peut pas voir. Donc si vous voulez des méthodes que le code en dehors de votre implémentation ne doit pas utiliser, vous n'avez pas le choix, le compilateur doit vous donner un outil pour que vous puissiez lui dire que la méthode ne doit pas être utilisée, c'est le mot clé "private".

En Objective-C, vous pouvez avoir des méthodes qui ne sont pas dans le fichier d'en-tête. Vous atteignez donc très facilement le même objectif en n'ajoutant pas la méthode au fichier d'en-tête. Il n'y a pas besoin de méthodes privées. Objective-C a également l'avantage que vous n'avez pas besoin de recompiler chaque utilisateur d'une classe car vous avez changé les méthodes privées.

Par exemple, les variables que vous deviez déclarer dans le fichier d'en-tête (plus maintenant), @private, @public et @protected sont disponibles.

gnasher729
la source
0

Une réponse manquante ici est: parce que les méthodes privées sont une mauvaise idée du point de vue de l'évolutivité. Cela peut sembler une bonne idée de rendre une méthode privée lors de son écriture, mais c'est une forme de liaison précoce. Le contexte peut changer et un utilisateur ultérieur peut souhaiter utiliser une implémentation différente. Un peu provocateur: "Les développeurs agiles n'utilisent pas de méthodes privées"

D'une certaine manière, tout comme Smalltalk, Objective-C est destiné aux programmeurs adultes. Nous apprécions de savoir ce que le développeur d'origine a supposé que l'interface devrait être, et nous prenons la responsabilité de gérer les conséquences si nous devons modifier l'implémentation. Alors oui, c'est de la philosophie, pas de la mise en œuvre.

Stéphan Eggermont
la source
J'ai voté à la hausse parce qu'il ne mérite pas le vote à la baisse. Il y a, comme le dit Stephan, un argument selon lequel les méthodes "privées" ne sont pas nécessaires, et il est similaire aux arguments sur le typage dynamique vs statique, en ce que, quelle que soit votre conclusion préférée, les deux côtés ont un point.
alastair