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

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

Un certain nombre de mots-clés sont appliqués aux ivars (membres) qui contrôlent leur étendue, par exemple @private , @public , @protected . Pourquoi cela ne peut-il pas être fait pour les méthodes aussi? Cela semble être quelque chose que le moteur d’exécution devrait pouvoir supporter. Y a-t-il une philosophie sous-jacente qui me manque? Est-ce délibéré?

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

Objective-C est purement dynamic au moment de l’envoi de la méthode. En particulier, chaque envoi de méthode passe par le même sharepoint résolution de méthode dynamic que chaque autre envoi de méthode. Lors 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 les méthodes et les sélecteurs de la même manière pour toutes les méthodes.

Comme beaucoup ont répondu (à la fois ici et dans d’autres questions), les méthodes privées de compilation sont sockets en charge; Si une classe ne déclare pas de méthode dans son interface publiquement disponible, cette méthode pourrait également ne pas exister en ce qui concerne votre code. En d’autres termes, vous pouvez réaliser 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 appendait énormément de complexité et de frais généraux. Et même avec toute cette complexité, cela n’empêcherait pas tous les développeurs, sauf les plus occasionnels, d’exécuter vos méthodes soi-disant “privées”.

EDIT: L’une des hypothèses que j’ai remarquées est que les messages privés doivent passer par le runtime, ce qui peut entraîner une surcharge 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 l’ensemble des fonctionnalités d’Objective-C dans l’implémentation, ce qui signifie qu’une dissortingbution dynamic doit avoir lieu. Cependant , il n’y a aucune raison particulière pour laquelle les méthodes privées ne pourraient pas être envoyées par une variante spéciale de objc_msgSend() , car le compilateur saurait qu’elles sont privées; Cela peut être réalisé en ajoutant un tableau de méthode uniquement privé à la structure de Class .

Une méthode privée ne pourrait pas court-circuiter cette vérification ou ignorer le temps d’exécution?

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

Cela dit, il n’ya aucune raison pour qu’un tiers ne puisse pas délibérément appeler objc_msgSendPrivate() sur un object, en dehors de l’implémentation de cet object, et certaines choses (KVO, par exemple) devraient le faire. En effet, ce serait juste une convention et un peu 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.

Le faire, cependant, nuirait à la nature dynamic pure de la langue. Chaque envoi de méthode ne serait plus soumis à un mécanisme de répartition identique. Au lieu de cela, vous seriez dans une situation où la plupart des méthodes se comportent d’une manière et une petite poignée sont simplement différentes.

Cela va au-delà de l’exécution car il existe de nombreux mécanismes dans Cocoa qui s’ajoutent au dynamisme constant d’Objective-C. Par exemple, le codage de la valeur clé et l’observation de la valeur de la clé doivent être très fortement modifiés pour prendre en charge les méthodes privées – probablement en créant une faille exploitable – ou les méthodes privées seraient incompatibles.

Le runtime pourrait le supporter mais le coût serait énorme. Chaque sélecteur envoyé doit être vérifié pour savoir s’il est privé ou public pour cette classe ou si chaque classe doit 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 runtime 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 utilisait instanceMethodForSelector: cela pourrait donner à l’ IMP retourné toute autre classe pour qu’ils invoquent directement la méthode privée.

Les méthodes privées ne pouvaient pas contourner l’envoi de messages. Considérez le scénario suivant:

  1. Une classe AllPublic a une méthode d’instance publique doSomething

  2. Une autre classe HasPrivate a une méthode d’instance privée également appelée doSomething

  3. Vous créez un tableau contenant un nombre quelconque d’instances de AllPublic et HasPrivate

  4. Vous avez la boucle suivante:

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

    Si vous AllPublic cette boucle depuis AllPublic , le runtime devrait vous empêcher d’envoyer doSomething aux instances de HasPrivate , mais cette boucle serait utilisable si elle se trouvait dans la classe HasPrivate .

Les réponses apscopes jusqu’ici font un bon travail pour répondre à la question d’un sharepoint vue philosophique, alors je vais poser une raison plus pragmatique: que gagnerait-il en changeant la sémantique de la langue? C’est assez simple pour “masquer” efficacement les méthodes privées. À titre d’exemple, imaginons qu’une classe soit 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 placer 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 il est suffisamment proche, alors pourquoi modifier la sémantique du langage, ainsi que le compilateur, le runtime, etc. pour append une fonctionnalité déjà émulée façon? Comme indiqué dans d’autres réponses, la sémantique de transmission de messages – et leur dépendance à la reflection à l’exécution – rendrait la manipulation de messages “privés” non sortingviaux.

La solution la plus simple consiste simplement à déclarer certaines fonctions C statiques dans vos classes Objective-C. Celles-ci ont uniquement une scope de fichier conforme aux 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 chichi du tout.

Oui, cela peut être fait sans affecter le runtime en utilisant une technique déjà utilisé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) peuvent contourner suffisamment. IOW, vous avez besoin de plus de douleur pour surmonter les habitudes enracinées.

Vous pouvez append des correctifs à clang ou gcc qui ajoutent des méthodes privées à la syntaxe et génèrent des noms mutilés reconnus par eux-mêmes lors de la compilation (et rapidement oubliés). Les autres membres de la communauté Objective-C seraient alors en mesure de déterminer si cela en valait la peine ou non. Cela risque d’être plus rapide que d’essayer de convaincre les développeurs.

Essentiellement, cela a à voir avec la forme de passage de messages d’Objective-C des appels de méthode. Tout message peut être envoyé à n’importe quel object et l’object choisit comment répondre au message. Normalement, il répondra en exécutant la méthode nommée après le message, mais il pourrait également répondre de plusieurs 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 un peu gênant.

Même l’implémentation de méthodes privées par Ruby est un peu déroutante pour les personnes en raison de l’étrangeté (vous pouvez envoyer à l’object n’importe quel message, sauf ceux de cette liste !). Essentiellement, Ruby fait en sorte que les méthodes privées soient 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.

C’est un problème avec l’environnement d’exécution d’Objective-C. Alors que C / C ++ se compilait en code machine illisible, Objective-C conserve toujours certains atsortingbuts lisibles par l’homme, tels que les noms de méthode, sous forme de chaînes . Ceci donne à Objective-C la capacité d’exécuter des fonctions de reflection .

EDIT: Etre un langage de reflection sans méthodes privées ssortingctes rend Objective-C plus “pythonique” en ce sens que vous faites confiance à d’autres personnes qui utilisent votre code plutôt que de restreindre les méthodes qu’ils peuvent appeler. Utiliser des conventions de dénomination telles que le double soulignement permet de masquer votre code à un codeur client occasionnel, mais n’empêchera pas les codeurs de faire un travail plus sérieux.

Il y a deux réponses en fonction de l’interprétation de la question.

La première consiste à masquer l’implémentation de la méthode à partir de l’interface. Ceci est utilisé, généralement avec une catégorie sans nom (par exemple, @interface Foo() ). Cela permet à l’object d’envoyer ces messages, mais pas les autres – même si on peut toujours les remplacer accidentellement (ou autrement).

La deuxième réponse, en supposant que cela concerne les performances et l’inclusion, est rendue possible mais en tant que fonction C locale. Si vous vouliez une méthode ‘private foo ( NSSsortingng *arg )’, vous void MyClass_foo(MyClass *self, NSSsortingng *arg) et l’ MyClass_foo(self,arg) comme une fonction C comme MyClass_foo(self,arg) . La syntaxe est différente, mais elle agit avec le même type de performances que les 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 méthode Objective-C la plus courante.

Objective-C ne supporte pas 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 des méthodes que quelqu’un, y compris le fichier d’en-tête, ne peut pas voir. Donc, si vous voulez que les méthodes que le code en dehors de votre implémentation ne devrait pas utiliser, vous n’avez pas le choix, le compilateur doit vous donner un outil pour que vous puissiez 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 le même objective très facilement 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 de ne pas avoir à recomstackr chaque utilisateur d’une classe car vous avez modifié les méthodes privées.

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

Une réponse manquante est la suivante: les méthodes privées sont une mauvaise idée du sharepoint vue de l’évolutivité. Cela peut sembler une bonne idée de rendre une méthode privée lors de l’écriture, mais c’est une forme de liaison anticipée. Le contexte peut changer et un utilisateur ultérieur peut vouloir 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 s’adresse aux programmeurs adultes. Nous apprécions de savoir ce que le développeur d’origine a supposé être l’interface et de prendre la responsabilité de gérer les conséquences si nous devons modifier la mise en œuvre. Alors oui, c’est la philosophie, pas la mise en œuvre.