wake-up-neo.com

Warum unterstützt Objective-C keine privaten Methoden?

Ich habe in Objective-C eine Reihe von Strategien zum Deklarieren von halbprivaten Methoden gesehen, aber es scheint keine Möglichkeit 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, die auf ivars (Mitglieder) angewendet werden und deren Umfang steuern, z. @private, @public, @protected. Warum ist dies nicht auch für Methoden möglich? Es scheint etwas zu sein, das die Laufzeit unterstützen kann. Gibt es eine Philosophie, die mir fehlt? Ist das absichtlich?

122
Rob Jones

Die Antwort ist ... na ja ... einfach. Einfachheit und Konsistenz.

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

Wie viele (hier und 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 ebenso wenig vorhanden. Mit anderen Worten, Sie können alle zur Kompilierungszeit gewünschten verschiedenen Sichtbarkeitskombinationen erzielen, indem Sie Ihr Projekt entsprechend organisieren.

Es ist wenig vorteilhaft, dieselbe Funktionalität in die Laufzeit zu duplizieren. Dies würde eine enorme Menge an Komplexität und Overhead mit sich bringen. Und selbst bei all dieser Komplexität würde es nur den gelegentlichsten Entwickler daran hindern, Ihre vermeintlich "privaten" Methoden auszuführen.

BEARBEITEN: Eine der Annahmen, die ich bemerkt habe, ist, dass private Nachrichten die Laufzeit durchlaufen müssten, was zu einem potenziell großen Overhead führen würde. Ist das absolut wahr?

Ja ist es. Es gibt keinen Grund anzunehmen, dass der Implementierer einer Klasse nicht alle in der Implementierung enthaltenen Objective-C-Features verwenden möchte. Dies bedeutet, dass der dynamische Versand erfolgen muss. Allerdings, es gibt keinen besonderen Grund, warum private Methoden nicht von einer speziellen Variante von objc_msgSend() ausgelöst werden konnten, da der Compiler wissen würde, dass sie privat sind. dies könnte erreicht werden, indem der Class -Struktur 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 würde muss unbedingt nach privaten Methoden suchen.

Das heißt, es gibt keinen Grund, warum ein Dritter objc_msgSendPrivate() nicht absichtlich für ein Objekt außerhalb der Implementierung dieses Objekts 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 etwas besser, als den Selektoren privater Methoden ein Präfix zu setzen oder sie nicht im Interface-Header zu erwähnen.

Dies würde jedoch die reine Dynamik der Sprache untergraben. Nicht mehr jede Versandmethode würde einen identischen Versandmechanismus durchlaufen. Stattdessen würden Sie in einer Situation zurückbleiben, in der sich die meisten Methoden auf eine Weise verhalten und eine kleine Handvoll einfach anders ist.

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

102
bbum

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 ist beispielsweise bei Variablen nicht der Fall, da diese Schutzstufe zur Kompilierungszeit ausgeführt wird.

Außerdem müsste die Laufzeitumgebung überprüfen, ob der Absender einer privaten Nachricht derselben Klasse angehört wie der Empfänger. Sie können auch private Methoden umgehen. wenn die Klasse instanceMethodForSelector:, es könnte die zurückgegebene IMP einer anderen Klasse geben, damit diese die private Methode direkt aufruft.

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

  1. Eine Klasse AllPublic hat eine öffentliche Instanzmethode doSomething

  2. Eine andere Klasse HasPrivate hat eine private Instanzmethode, die auch als doSomething bezeichnet wird.

  3. Sie erstellen ein Array mit einer beliebigen Anzahl von Instanzen von AllPublic und HasPrivate.

  4. Sie haben folgende Schleife:

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

    Wenn Sie diese Schleife innerhalb von AllPublic ausführen, müsste die Laufzeit verhindern, dass Sie doSomething für die HasPrivate - Instanzen senden. Diese Schleife wäre jedoch verwendbar, wenn sie sich innerhalb von HasPrivate Klasse.

18
dreamlax

Die bisher veröffentlichten Antworten sind eine gute Antwort auf die Frage aus philosophischer Sicht. Ich gehe daher von einem pragmatischeren Grund aus: Was würde durch eine Änderung der Semantik der Sprache erreicht werden? Es ist einfach genug, um private Methoden effektiv zu "verstecken". Stellen Sie sich zum Beispiel vor, Sie hätten eine Klasse in einer Header-Datei deklariert:

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

Wenn Sie "private" Methoden benötigen, können Sie dies 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 ziemlich das Gleiche wie private C++/Java-Methoden, aber es ist effektiv nahe genug, warum sollten Sie also die Semantik der Sprache sowie den Compiler, die Laufzeit usw. ändern, um a hinzuzufügen? Funktion, die bereits in akzeptabler Weise emuliert ist? Wie in anderen Antworten angemerkt, würde die Semantik der Nachrichtenübermittlung - und ihre Abhängigkeit von der Laufzeitreflexion - den Umgang mit "privaten" Nachrichten nicht trivial machen.

13
mipadi

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 kein Aufhebens.

7
Cromulent

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

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

Sie könnten Patches zu clang oder gcc hinzufügen, die der Syntax private Methoden hinzufügen und unleserliche Namen generieren, die nur beim Kompilieren erkannt (und umgehend vergessen) wurden. Dann könnten andere Mitglieder der Objective-C-Community feststellen, ob es sich wirklich lohnt oder nicht. Das ist wahrscheinlich schneller als der Versuch, die Entwickler zu überzeugen.

6
Huperniketes

Im Wesentlichen hat es mit der Message-Passing-Form von Methodenaufrufen in Objective-C zu tun. Jede Nachricht kann an jedes Objekt gesendet werden, und das Objekt wählt aus, wie auf die Nachricht geantwortet werden soll. Normalerweise reagiert es, indem es die nach der Nachricht benannte Methode ausführt, aber es kann auch auf verschiedene andere Arten reagieren. 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 beliebige Nachricht senden, mit Ausnahme der in dieser Liste aufgeführten!). Grundsätzlich verhindert Ruby, dass private Methoden mit einem expliziten Empfänger aufgerufen werden. In Objective-C würde dies noch mehr Arbeit erfordern, da Objective-C diese Option nicht hat.

4
Chuck

Dies ist ein Problem mit der Laufzeitumgebung von Objective-C. Während C/C++ in nicht lesbaren Maschinencode kompiliert, Objective-C behält weiterhin einige für den Menschen lesbare Attribute wie Methodennamen als Zeichenfolgen bei . Dadurch kann Objective-C reflektierende Funktionen ausführen.

BEARBEITEN: Wenn Sie eine reflektierende Sprache ohne strenge private Methoden sind, wird Objective-C "pythonischer", da Sie anderen Personen vertrauen, die Ihren Code verwenden, anstatt zu beschränken, was Methoden, die sie aufrufen können. Die Verwendung von Namenskonventionen wie doppelte Unterstriche soll Ihren Code vor einem zufälligen Client-Codierer verbergen, aber nicht verhindern, dass Codierer ernsthaftere Arbeit leisten müssen.

1
pokstad

Je nach Auslegung der Frage gibt es zwei Antworten.

Die erste besteht darin, die Methodenimplementierung vor der Schnittstelle zu verbergen. Dies wird normalerweise für eine Kategorie ohne Namen verwendet (z. B. @interface Foo()). Auf diese Weise kann das Objekt diese Nachrichten senden, jedoch keine anderen. Möglicherweise wird dies jedoch versehentlich (oder auf andere Weise) außer Kraft gesetzt.

Die zweite Antwort wird unter der Annahme möglich gemacht, dass es um Leistung und Inlining geht, sondern als lokale C-Funktion. Wenn Sie eine private foo (NSString *arg) -Methode wollten, würden Sie void MyClass_foo(MyClass *self, NSString *arg) ausführen und diese als C-Funktion wie MyClass_foo(self,arg) aufrufen. Die Syntax ist unterschiedlich, entspricht jedoch den Leistungsmerkmalen der privaten C++ - Methoden.

Obwohl dies die Frage beantwortet, sollte ich darauf hinweisen, dass die No-Name-Kategorie bei weitem die gebräuchlichste Methode ist, dies mit Objective-C zu tun.

1
AlBlue

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

In C++ muss jede Methode in der Deklaration der Klasse sichtbar sein. Sie können keine Methoden haben, die jemand mit der Header-Datei nicht sehen kann. Wenn Sie also Methoden verwenden möchten, die außerhalb Ihrer Implementierung nicht verwendet werden sollen, haben Sie keine andere Wahl. Der Compiler muss Ihnen ein Tool zur Verfügung stellen, mit dem Sie angeben können, dass die Methode nicht verwendet werden darf. Dies ist das Schlüsselwort "private".

In Objective-C können Methoden vorhanden sein, die nicht in der Headerdatei enthalten sind. 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, weil Sie private Methoden geändert haben.

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

0
gnasher729

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 provokant: "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, nicht Umsetzung.

0