Warum unterstützt Objective-C keine privaten Methoden?

123

Ich habe in Objective-C eine Reihe von Strategien zur Deklaration halbprivater Methoden gesehen , aber es scheint keinen Weg zu geben, eine wirklich private Methode zu erstellen. Ich akzeptiere das. Aber warum ist das so? Jede Erklärung, die ich im Wesentlichen gesagt habe: "Sie können es nicht tun, aber hier ist eine enge Annäherung."

Es gibt eine Reihe von Schlüsselwörtern angewendet ivars(Mitglieder) , die ihren Umfang zu steuern, zum Beispiel @private, @public, @protected. Warum kann dies nicht auch für Methoden durchgeführt werden? Es scheint etwas zu sein, das die Laufzeit unterstützen sollte. Gibt es eine zugrunde liegende Philosophie, die mir fehlt? Ist das absichtlich?

Rob Jones
quelle
15
Gute Frage übrigens.
bbum
5
Zu Ihren Änderungen: Ja, die Laufzeitdurchsetzung wäre sehr teuer und für sehr wenig Nutzen. Die Durchsetzung während der Kompilierungszeit wäre eine willkommene Ergänzung der Sprache und durchaus erreichbar. Ja, es könnte zur Laufzeit umgangen werden, aber das ist in Ordnung. Der Programmierer ist kein Feind. Das Ziel von scope ist es, den Programmierer vor Fehlern zu schützen.
Rob Napier
1
Sie können die Laufzeit nicht "überspringen" - die Laufzeit ist das, was den Nachrichtenversand bewirkt.
Chuck
"Es gibt keine Möglichkeit für eine private Methode, diese Prüfung kurzzuschließen. " Meinten Sie "umgehen"? ;-)
Constantino Tsarouhas

Antworten:

103

Die Antwort ist ... na ja ... einfach. Einfachheit und Konsistenz in der Tat.

Objective-C ist zum Zeitpunkt des Versands der Methode rein dynamisch. Insbesondere durchläuft jeder Methodenversand genau den gleichen dynamischen Methodenauflösungspunkt wie jeder andere Methodenversand. Zur Laufzeit hat jede Methodenimplementierung genau die gleiche Exposition und alle von der Objective-C-Laufzeit bereitgestellten APIs, die mit Methoden und Selektoren arbeiten, funktionieren für alle Methoden gleich.

Wie viele (sowohl hier als auch in anderen Fragen) geantwortet haben, werden private Methoden zur Kompilierungszeit unterstützt. Wenn eine Klasse keine Methode in ihrer öffentlich verfügbaren Schnittstelle deklariert, ist diese Methode für Ihren Code möglicherweise nicht vorhanden. Mit anderen Worten, Sie können alle verschiedenen Kombinationen der Sichtbarkeit erzielen, die zur Kompilierungszeit gewünscht werden, indem Sie Ihr Projekt entsprechend organisieren.

Das Duplizieren derselben Funktionalität in die Laufzeit hat wenig Vorteile. Es würde eine enorme Menge an Komplexität und Overhead hinzufügen. Und selbst bei all dieser Komplexität würde es nicht alle außer den gelegentlichsten Entwicklern daran hindern, Ihre angeblich "privaten" Methoden auszuführen.

BEARBEITEN: Eine der Annahmen, die mir aufgefallen sind, ist, dass private Nachrichten die Laufzeit durchlaufen müssen, was zu einem potenziell hohen Overhead führen kann. Ist das absolut wahr?

Ja, so ist es. Es gibt keinen Grund anzunehmen, dass der Implementierer einer Klasse nicht alle in der Implementierung enthaltenen Objective-C-Funktionen verwenden möchte, und dies bedeutet, dass ein dynamischer Versand erfolgen muss. Es gibt jedoch keinen besonderen Grund, warum private Methoden nicht mit einer speziellen Variante von ausgeliefert werden könnten objc_msgSend(), da der Compiler wissen würde, dass sie privat sind. Dies könnte erreicht werden, indem der ClassStruktur eine Nur-Privat-Methodentabelle hinzugefügt wird .

Es gäbe keine Möglichkeit für eine private Methode, diese Prüfung kurzzuschließen oder die Laufzeit zu überspringen.

Die Laufzeit konnte nicht übersprungen werden, aber die Laufzeit musste nicht unbedingt nach privaten Methoden suchen.

Es gibt jedoch keinen Grund, warum ein Dritter objc_msgSendPrivate()ein Objekt außerhalb der Implementierung dieses Objekts nicht absichtlich aufrufen könnte , und einige Dinge (z. B. KVO) müssten dies tun. Tatsächlich wäre dies nur eine Konvention und in der Praxis kaum besser, als den Selektoren privater Methoden ein Präfix voranzustellen oder sie im Schnittstellenheader nicht zu erwähnen.

Dies würde jedoch die reine Dynamik der Sprache untergraben. Nicht mehr würde jeder Methodenversand einen identischen Versandmechanismus durchlaufen. Stattdessen würden Sie in einer Situation zurückbleiben, in der sich die meisten Methoden in eine Richtung verhalten und eine kleine Handvoll nur anders sind.

Dies geht über die Laufzeit hinaus, da es in Cocoa viele Mechanismen gibt, die auf der konsistenten Dynamik von Objective-C aufbauen. Beispielsweise müssten sowohl die Schlüsselwertcodierung als auch die Schlüsselwertbeobachtung entweder stark modifiziert werden, um private Methoden zu unterstützen - höchstwahrscheinlich durch Schaffung einer ausnutzbaren Lücke - oder private Methoden wären nicht kompatibel.

bbum
quelle
49
+1 Objective-C ist (in gewisser Weise) eine "Big-Boy" -Sprache, in der von Programmierern ein gewisses Maß an Disziplin erwartet wird, anstatt auf Schritt und Tritt vom Compiler / der Laufzeit auf die Hand geschlagen zu werden. Ich finde es erfrischend. Für mich ist es ausreichend und vorzuziehen, die Sichtbarkeit der Methoden beim Kompilieren / Verknüpfen einzuschränken.
Quinn Taylor
11
Mehr Python als "obj-c-ic" :). Guido war sehr proaktiv bei der Wartung von Python auf NeXT-Systemen, einschließlich der Erstellung der ersten Version von PyObjC. Somit hat ObjC Python etwas beeinflusst .
bbum
3
+1 für die interessante historische Geschichte des wohlwollenden Diktators fürs Leben und seine Kreuzungen mit dem mythischen NeXT-System.
Pokstad
5
Danke dir. Python, Java und Objective-C haben alle Laufzeitobjektmodelle, die [ähnlich wie SmallTalk] verdammt ähnlich sind. Java wurde ganz direkt von Objective-C abgeleitet. Python hat mehr als nur einen Parallelkurs zur Inspiration absolviert, aber Guido hat NeXTSTEP auf jeden Fall genau studiert.
bbum
1
Außer ich vertraue seiner Lizenz nicht und würde definitiv Clang gegenüber GCC vorziehen. Aber es ist sicher ein guter erster Schritt.
Cthutu
18

Die Laufzeit könnte es unterstützen, aber die Kosten wären enorm. Jeder gesendete Selektor müsste überprüft werden, ob er für diese Klasse privat oder öffentlich ist, oder jede Klasse müsste zwei separate Versandtabellen verwalten. Dies gilt beispielsweise nicht für Variablen, da diese Schutzstufe zur Kompilierungszeit ausgeführt wird.

Außerdem müsste die Laufzeit überprüfen, ob der Absender einer privaten Nachricht zur selben Klasse gehört wie der Empfänger. Sie können auch private Methoden umgehen. Wenn die Klasse verwendet wird instanceMethodForSelector:, kann sie die Rückgabe IMPan eine andere Klasse weitergeben, damit diese die private Methode direkt aufrufen kann.

Private Methoden konnten den Nachrichtenversand nicht umgehen. Stellen Sie sich das folgende Szenario vor:

  1. Eine Klasse AllPublicverfügt über eine öffentliche InstanzmethodedoSomething

  2. Eine andere Klasse HasPrivatehat eine private Instanzmethode, die auch aufgerufen wirddoSomething

  3. Sie erstellen ein Array, das eine beliebige Anzahl von Instanzen von AllPublicund enthältHasPrivate

  4. Sie haben die folgende Schleife:

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

    Wenn Sie diese Schleife von innen AllPublicausführen würden, müsste die Laufzeit das Senden doSomethingder HasPrivateInstanzen stoppen. Diese Schleife wäre jedoch verwendbar, wenn sie sich innerhalb der HasPrivateKlasse befindet.

Dreamlax
quelle
1
Ich bin damit einverstanden, dass dies mit Kosten verbunden wäre. Vielleicht ist es nicht etwas, was Sie auf einem Handheld-Gerät wollen. Können Sie das enorme Qualifikationsspiel sonst unterstützen? Würden die Kosten auf einem Desktop-System wirklich eine Rolle spielen?
Rob Jones
1
Ich habe es zwar nicht getan, aber Sie haben vielleicht Recht. Objective-C hat einen Laufzeitnachrichtenversand im Vergleich zum Methodenaufruf zur Kompilierungszeit von C ++, sodass Sie von einer Überprüfung der Kompilierungszeit im Vergleich zu einer Laufzeitprüfung sprechen würden.
Remus Rusanu
Es scheint wirklich seltsam, dass der Hauptgrund dafür, dass private Methoden nicht unterstützt werden, in der Leistung liegt. Ich denke, Apple hielt private Methoden einfach nicht für sehr notwendig, unabhängig davon, ob es einen Leistungseinbruch gab oder nicht. Andernfalls hätten sie eine andere Sprache für ihr Milliarden-Dollar-Unternehmen wählen sollen.
Pokstad
1
Das Hinzufügen privater Methoden würde nicht nur die Implementierung der Laufzeit erschweren, sondern auch die Semantik der Sprache. Die Zugriffskontrolle ist ein einfaches Konzept in statischen Sprachen, aber es passt nicht zum dynamischen Versand, der mit viel konzeptionellem Aufwand verbunden ist und zu vielen, vielen „Warum funktioniert das nicht?“ Führt. Fragen.
Jens Ayton
7
Was würden Ihnen private Methoden wirklich kaufen? Objective-C ist schließlich eine C-basierte Sprache. Wenn also jemand Code in Ihrem Prozess ausführt, kann er Ihren Prozess problemlos p0wnz, unabhängig davon, wie privat oder verschleiert eine API sein mag.
bbum
13

Die bisher veröffentlichten Antworten beantworten die Frage aus philosophischer Sicht gut, daher werde ich einen pragmatischeren Grund nennen: Was würde durch eine Änderung der Semantik der Sprache erreicht werden? Es ist einfach genug, private Methoden effektiv zu "verstecken". Stellen Sie sich beispielsweise vor, Sie haben eine Klasse in einer Header-Datei deklariert, wie folgt:

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

Wenn Sie "private" Methoden benötigen, können Sie diese auch in die Implementierungsdatei einfügen:

@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

Sicher, es ist nicht ganz dasselbe wie private C ++ / Java-Methoden, aber es ist effektiv nah genug. Warum also die Semantik der Sprache sowie den Compiler, die Laufzeit usw. ändern, um eine Funktion hinzuzufügen, die bereits in einer akzeptablen Form emuliert ist? Weg? Wie in anderen Antworten erwähnt, würde die Semantik der Nachrichtenübermittlung - und ihre Abhängigkeit von der Laufzeitreflexion - den Umgang mit "privaten" Nachrichten nicht trivial machen.

Mipadi
quelle
1
Das Problem bei diesem Ansatz ist, dass jemand private Methoden (auch ohne es zu wissen) überschreiben kann, wenn er Ihre Klasse unterordnet. Ich nehme an, im Wesentlichen müssen Sie Namen auswählen, die wahrscheinlich nicht überschrieben werden.
Dreamlax
3
Was sich für mich wie ein Hack auf einem Hack anfühlt.
Rob Jones
1
@Mike: Apple hat angegeben, dass Methodennamen mit führenden Unterstrichen ausdrücklich nur für Apple reserviert sind: developer.apple.com/mac/library/documentation/Cocoa/Conceptual/… . Die empfohlene Alternative besteht darin, zwei Großbuchstaben und dann einen Unterstrich voranzustellen.
Dreamlax
8
Geben Sie außerdem Ihre SSN nach der Methode ein, damit andere Programmierer wissen, wer sie geschrieben hat. = D Namespaces, brauche sie !!!
Pokstad
2
Wenn Sie sich Sorgen darüber machen, dass die von Ihnen definierten Methoden überschrieben werden, haben Sie die inhärente Ausführlichkeit, die Objective-C fördert, nicht richtig
berücksichtigt
7

Die einfachste Lösung besteht darin, einige statische C-Funktionen in Ihren Objective-C-Klassen zu deklarieren. Diese haben nur den Dateibereich gemäß den C-Regeln für das statische Schlüsselwort und können daher nur von Methoden in dieser Klasse verwendet werden.

Überhaupt keine Aufregung.

Cromulent
quelle
Müssen diese außerhalb des Abschnitts @implementation definiert werden?
Rob Jones
@Rob - nein, sie können (und sollten höchstwahrscheinlich) in Ihrer Klassenimplementierung definiert werden.
Cromulent
6

Ja, dies kann ohne Beeinträchtigung der Laufzeit erfolgen, indem eine Technik verwendet wird, die bereits von den Compilern für die Behandlung von C ++ verwendet wird: Namensveränderung.

Es wurde nicht getan, weil nicht festgestellt wurde, dass es einige erhebliche Schwierigkeiten im Codierungsproblemraum lösen würde, die andere Techniken (z. B. Präfixieren oder Unterstreichen) ausreichend umgehen können. IOW, du brauchst mehr Schmerz, um tief verwurzelte Gewohnheiten zu überwinden.

Sie können Patches zu clang oder gcc hinzufügen, die der Syntax private Methoden hinzufügen und verstümmelte Namen generieren, die sie allein beim Kompilieren erkannt (und sofort vergessen) haben. Dann könnten andere Mitglieder der Objective-C-Community feststellen, ob es sich tatsächlich lohnt oder nicht. Auf diese Weise ist es wahrscheinlich schneller als der Versuch, die Entwickler zu überzeugen.

Huperniketes
quelle
2
Das Mangeln von Namen in der Kompilierungszeit ist viel schwieriger, als man annehmen könnte, da Sie alle Methodenselektoren entstellen müssen, einschließlich derjenigen, die in XIB-Dateien angezeigt werden und die möglicherweise an NSSelectorFromString () sowie KeyValueCoding & friends ...
bbum
1
Ja, es wäre aufwändiger, wenn Sie private Methoden in der gesamten Toolchain unterstützen möchten. Die Symboltabelle des Compilers müsste gespeichert und über eine Toolchain-API zugänglich sein. Es wäre nicht das erste Mal, dass Apple den Entwicklern schrittweise Funktionen zur Verfügung stellen müsste, die so wichtig sind, wie es Zeit und Stabilität erlauben. Bei privaten Methoden ist es jedoch fraglich, ob es ein ausreichender Gewinn ist, um die Anstrengungen zu unternehmen. Es ist sogar noch verdächtiger, dass sie durch diese anderen Mechanismen außerhalb der Klasse selbst zugänglich sein sollten.
Huperniketes
1
Wie würde eine Nur-Kompilierungs-Durchsetzung + Namensverfälschung jemanden davon abhalten, die Methodenliste einer Klasse zu durchsuchen und dort zu finden?
Dreamlax
Es würde nicht. Meine Antwort bezieht sich nur auf die vom OP gestellte Frage.
Huperniketes
Ich habe darüber nachgedacht, private Methoden zu Clang hinzuzufügen. Ein Grund, warum ich dachte, dass private Methoden gut wären, ist, dass sie direkt wie einfache C-Funktionen aufgerufen werden können und dann alle üblichen C-Funktionsoptimierungen stattfinden können. Ich habe mich aus Zeitgründen nie darum gekümmert, und Sie können dies bereits tun, indem Sie einfach c verwenden.
Nathan Day
4

Im Wesentlichen hat dies mit der Nachrichtenübermittlungsform von Objective-C für Methodenaufrufe zu tun. Jede Nachricht kann an jedes Objekt gesendet werden, und das Objekt wählt aus, wie auf die Nachricht reagiert werden soll. Normalerweise wird die nach der Nachricht benannte Methode ausgeführt, es kann jedoch auch auf andere Weise reagiert werden. Dies macht private Methoden nicht völlig unmöglich - Ruby macht es mit einem ähnlichen Nachrichtenübermittlungssystem - aber es macht sie etwas umständlich.

Sogar Rubys Implementierung privater Methoden ist für die Leute aufgrund der Seltsamkeit etwas verwirrend (Sie können dem Objekt jede Nachricht senden, die Sie mögen, außer den auf dieser Liste !). Im Wesentlichen funktioniert Ruby so, dass private Methoden nicht mit einem expliziten Empfänger aufgerufen werden dürfen. In Objective-C würde es noch mehr Arbeit erfordern, da Objective-C diese Option nicht hat.

Futter
quelle
1

Dies ist ein Problem mit der Laufzeitumgebung von Objective-C. Während C / C ++ in unlesbaren Maschinencode kompiliert wird, behält Objective-C einige für Menschen lesbare Attribute wie Methodennamen als Zeichenfolgen bei . Dies gibt Objective-C die Möglichkeit, reflektierende Merkmale auszuführen .

BEARBEITEN: Eine reflektierende Sprache ohne strenge private Methoden zu sein, macht Objective-C "pythonischer", da Sie anderen Personen vertrauen, die Ihren Code verwenden, anstatt einzuschränken, welche Methoden sie aufrufen können. Die Verwendung von Namenskonventionen wie doppelte Unterstriche soll Ihren Code vor einem gelegentlichen Client-Codierer verbergen, verhindert jedoch nicht, dass Codierer ernsthaftere Arbeiten ausführen müssen.

pokstad
quelle
Und wenn Verbraucher Ihrer Bibliothek Ihre privaten Methoden widerspiegeln könnten, könnten sie sie dann nicht auch nennen?
Jared Updike
Wenn sie wissen, wo sie suchen müssen, haben die Leute dann nicht undokumentierte Bibliotheken auf dem iPhone verwendet? Das Problem mit dem iPhone ist, dass Sie das Risiko eingehen, dass Ihre App nicht für die Verwendung einer privaten API akzeptiert wird.
Pokstad
Wenn sie durch Reflexion auf private Methoden zugreifen, bekommen sie, was sie verdienen. Fehler sind nicht deine Schuld.
Gnasher729
1

Abhängig von der Interpretation der Frage gibt es zwei Antworten.

Die erste besteht darin, die Methodenimplementierung vor der Schnittstelle zu verbergen. Dies wird normalerweise bei einer Kategorie ohne Namen verwendet (z @interface Foo(). B. ). Dadurch kann das Objekt diese Nachrichten senden, andere jedoch nicht - obwohl dies möglicherweise versehentlich (oder auf andere Weise) überschrieben wird.

Die zweite Antwort unter der Annahme, dass es sich um Leistung und Inlining handelt, wird ermöglicht, jedoch als lokale C-Funktion. Wenn Sie eine 'private foo ( NSString *arg)' - Methode wünschen , würden Sie void MyClass_foo(MyClass *self, NSString *arg)sie als C-Funktion wie ausführen MyClass_foo(self,arg). Die Syntax ist unterschiedlich, entspricht jedoch den normalen Leistungsmerkmalen der privaten Methoden von C ++.

Obwohl dies die Frage beantwortet, sollte ich darauf hinweisen, dass die Kategorie ohne Namen bei weitem die üblichere Objective-C-Methode ist, um dies zu tun.

AlBlue
quelle
0

Objective-C unterstützt keine privaten Methoden, da diese nicht benötigt werden.

In C ++ muss jede Methode in der Deklaration der Klasse sichtbar sein. Sie können keine Methoden haben, die jemand einschließlich der Header-Datei nicht sehen kann. Wenn Sie also Methoden wünschen, die Code außerhalb Ihrer Implementierung nicht verwenden sollte, haben Sie keine Wahl. Der Compiler muss Ihnen ein Tool zur Verfügung stellen, damit Sie ihm mitteilen können, dass die Methode nicht verwendet werden darf, dh das Schlüsselwort "private".

In Objective-C können Methoden vorhanden sein, die sich nicht in der Header-Datei befinden. Sie erreichen den gleichen Zweck also sehr einfach, indem Sie die Methode nicht zur Header-Datei hinzufügen. Private Methoden sind nicht erforderlich. Objective-C hat auch den Vorteil, dass Sie nicht jeden Benutzer einer Klasse neu kompilieren müssen, da Sie private Methoden geändert haben.

Beispielsweise sind Variablen verfügbar, die Sie früher in der Header-Datei deklarieren mussten (nicht mehr), @private, @public und @protected.

gnasher729
quelle
0

Eine fehlende Antwort lautet hier: Weil private Methoden aus Sicht der Evolvabilität eine schlechte Idee sind. Es mag eine gute Idee sein, eine Methode beim Schreiben privat zu machen, aber es ist eine Form der frühen Bindung. Der Kontext kann sich ändern, und ein späterer Benutzer möchte möglicherweise eine andere Implementierung verwenden. Ein bisschen provokativ: "Agile Entwickler verwenden keine privaten Methoden"

In gewisser Weise ist Objective-C genau wie Smalltalk für erwachsene Programmierer gedacht. Wir legen Wert darauf, zu wissen, wie der ursprüngliche Entwickler die Schnittstelle angenommen hat, und übernehmen die Verantwortung, mit den Konsequenzen umzugehen, wenn wir die Implementierung ändern müssen. Also ja, es ist Philosophie, keine Umsetzung.

Stephan Eggermont
quelle
Ich habe dies hochgestimmt, weil es die Abwertung nicht verdient. Es gibt, wie Stephan sagt, ein Argument, dass "private" Methoden nicht notwendig sind, und es ähnelt den Argumenten über dynamische und statische Typisierung, da unabhängig von Ihrer bevorzugten Schlussfolgerung beide Seiten einen Punkt haben.
Alastair