363 Stimmen

Der beste Weg, private Methoden für eine Klasse in Objective-C zu definieren

Ich habe gerade mit der Programmierung von Objective-C begonnen und frage mich, da ich einen Hintergrund in Java habe, wie Leute, die Objective-C-Programme schreiben, mit privaten Methoden umgehen.

Ich verstehe, dass es verschiedene Konventionen und Gewohnheiten geben kann, und betrachte diese Frage als einen Aggregator der besten Techniken, die Menschen im Umgang mit privaten Methoden in Objective-C verwenden.

Bitte geben Sie bei der Veröffentlichung eine Begründung für Ihren Ansatz an. Warum ist er gut? Welche Nachteile hat er (die Ihnen bekannt sind) und wie gehen Sie damit um?


Was meine bisherigen Erkenntnisse betrifft.

Es ist möglich, Folgendes zu verwenden Kategorien [z. B. MyClass (Private)] in der Datei MyClass.m definiert, um private Methoden zu gruppieren.

Dieser Ansatz hat 2 Probleme:

  1. Xcode (und Compiler?) prüft nicht, ob Sie alle Methoden in der Kategorie private im entsprechenden @implementation-Block definieren
  2. Sie müssen @interface, das Ihre private Kategorie deklariert, an den Anfang der Datei MyClass.m setzen, sonst beschwert sich Xcode mit einer Meldung wie "self may not respond to message "privateFoo".

Das erste Problem kann durch folgende Maßnahmen gelöst werden leere Kategorie [z.B. MyClass ()].
Der zweite Punkt stört mich sehr. Ich würde gerne private Methoden am Ende der Datei implementiert (und definiert) sehen; ich weiß nicht, ob das möglich ist.

443voto

Alex Punkte 26769

Wie bereits gesagt wurde, gibt es in Objective-C keine private Methode. Ab Objective-C 2.0 (d. h. Mac OS X Leopard, iPhone OS 2.0 und später) können Sie jedoch eine Kategorie mit einem leeren Namen erstellen (d. h. @interface MyClass () ) genannt Klasse Erweiterung . Das Besondere an einer Klassenerweiterung ist, dass die Methodenimplementierungen in dieselbe @implementation MyClass als die öffentlichen Methoden. Ich strukturiere meine Klassen also wie folgt:

In der .h-Datei:

@interface MyClass {
    // My Instance Variables
}

- (void)myPublicMethod;

@end

Und in der .m-Datei:

@interface MyClass()

- (void)myPrivateMethod;

@end

@implementation MyClass

- (void)myPublicMethod {
    // Implementation goes here
}

- (void)myPrivateMethod {
    // Implementation goes here
}

@end

Ich denke, der größte Vorteil dieses Ansatzes ist, dass Sie Ihre Methodenimplementierungen nach Funktionalität gruppieren können und nicht nach der (manchmal willkürlichen) Unterscheidung zwischen öffentlich und privat.

55voto

In Objective-C gibt es nicht wirklich eine "private Methode". Wenn die Laufzeitumgebung herausfinden kann, welche Implementierung zu verwenden ist, wird sie es tun. Das heißt aber nicht, dass es keine Methoden gibt, die nicht Teil der dokumentierten Schnittstelle sind. Für diese Methoden halte ich eine Kategorie für gut. Anstatt die @interface am Anfang der .m-Datei wie in Punkt 2, würde ich es in eine eigene .h-Datei packen. Eine Konvention, der ich folge (und die ich auch anderswo gesehen habe, ich glaube, es ist eine Apple-Konvention, da Xcode jetzt automatische Unterstützung dafür bietet), ist es, eine solche Datei nach ihrer Klasse und Kategorie zu benennen, mit einem + zwischen ihnen, also @interface GLObject (PrivateMethods) finden Sie in GLObject+PrivateMethods.h . Der Grund für die Bereitstellung der Header-Datei ist, dass Sie sie in Ihren Unit-Test-Klassen importieren können :-).

Übrigens, was die Implementierung/Definition von Methoden am Ende der .m-Datei betrifft, so können Sie das mit einer Kategorie tun, indem Sie die Kategorie am Ende der .m-Datei implementieren:

@implementation GLObject(PrivateMethods)
- (void)secretFeature;
@end

oder mit einer Klassenerweiterung (das, was Sie eine "leere Kategorie" nennen), definieren Sie diese Methoden einfach zuletzt. Objective-C-Methoden können in beliebiger Reihenfolge in der Implementierung definiert und verwendet werden, so dass nichts dagegen spricht, die "privaten" Methoden an das Ende der Datei zu setzen.

Auch bei Klassenerweiterungen erstelle ich oft eine separate Kopfzeile ( GLObject+Extension.h ), so dass ich diese Methoden bei Bedarf verwenden kann, um die Sichtbarkeit von "Freunden" oder "Geschützten" nachzuahmen.

Seit diese Antwort ursprünglich geschrieben wurde, hat der Clang-Compiler damit begonnen, zwei Durchläufe für Objective-C-Methoden durchzuführen. Das bedeutet, dass Sie es vermeiden können, Ihre "privaten" Methoden vollständig zu deklarieren, und egal, ob sie oberhalb oder unterhalb der aufrufenden Seite liegen, sie werden vom Compiler gefunden.

39voto

Andy Punkte 29060

Ich bin zwar kein Objective-C-Experte, aber ich persönlich definiere die Methode einfach in der Implementierung meiner Klasse. Zugegeben, sie muss vor (über) allen Methoden definiert werden, die sie aufrufen, aber das ist definitiv der geringste Arbeitsaufwand.

24voto

justin Punkte 103032

Definieren Sie Ihre privaten Methoden in der @implementation Block ist für die meisten Zwecke ideal. Clang sieht diese innerhalb der @implementation unabhängig von der Reihenfolge der Anmeldung. Es besteht keine Notwendigkeit, sie in einer Klassenfortsetzung (auch bekannt als Klassenerweiterung) oder benannten Kategorie zu deklarieren.

In einigen Fällen müssen Sie die Methode in der Klassenfortsetzung deklarieren (z. B. wenn Sie den Selektor zwischen der Klassenfortsetzung und der @implementation ).

static Funktionen sind sehr gut für besonders sensible oder geschwindigkeitskritische private Methoden geeignet.

Eine Konvention für die Benennung von Präfixen kann Ihnen helfen, ein versehentliches Überschreiben privater Methoden zu vermeiden (ich finde den Klassennamen als Präfix sicher).

Benannte Kategorien (z.B. @interface MONObject (PrivateStuff) ) sind wegen möglicher Namenskollisionen beim Laden keine besonders gute Idee. Sie sind wirklich nur nützlich für Freund- oder geschützte Methoden (die sehr selten eine gute Wahl sind). Um sicherzustellen, dass Sie vor unvollständigen Kategorieimplementierungen gewarnt werden, sollten Sie sie tatsächlich implementieren:

@implementation MONObject (PrivateStuff)
...HERE...
@end

Hier ist ein kleiner kommentierter Spickzettel:

MONObject.h

@interface MONObject : NSObject

// public declaration required for clients' visibility/use.
@property (nonatomic, assign, readwrite) bool publicBool;

// public declaration required for clients' visibility/use.
- (void)publicMethod;

@end

MONObject.m

@interface MONObject ()
@property (nonatomic, assign, readwrite) bool privateBool;

// you can use a convention where the class name prefix is reserved
// for private methods this can reduce accidental overriding:
- (void)MONObject_privateMethod;

@end

// The potentially good thing about functions is that they are truly
// inaccessible; They may not be overridden, accidentally used,
// looked up via the objc runtime, and will often be eliminated from
// backtraces. Unlike methods, they can also be inlined. If unused
// (e.g. diagnostic omitted in release) or every use is inlined,
// they may be removed from the binary:
static void PrivateMethod(MONObject * pObject) {
    pObject.privateBool = true;
}

@implementation MONObject
{
    bool anIvar;
}

static void AnotherPrivateMethod(MONObject * pObject) {
    if (0 == pObject) {
        assert(0 && "invalid parameter");
        return;
    }

    // if declared in the @implementation scope, you *could* access the
    // private ivars directly (although you should rarely do this):
    pObject->anIvar = true;
}

- (void)publicMethod
{
    // declared below -- but clang can see its declaration in this
    // translation:
    [self privateMethod];
}

// no declaration required.
- (void)privateMethod
{
}

- (void)MONObject_privateMethod
{
}

@end

Ein weiterer Ansatz, der vielleicht nicht offensichtlich ist: Ein C++-Typ kann sowohl sehr schnell sein als auch ein viel höheres Maß an Kontrolle bieten, während die Anzahl der exportierten und geladenen objc-Methoden minimiert wird.

14voto

dreamlax Punkte 91447

Sie könnten versuchen, eine statische Funktion unterhalb oder oberhalb Ihrer Implementierung zu definieren, die einen Zeiger auf Ihre Instanz aufnimmt. Sie wird auf alle Variablen Ihrer Instanz zugreifen können.

//.h file
@interface MyClass : Object
{
    int test;
}
- (void) someMethod: anArg;

@end

//.m file    
@implementation MyClass

static void somePrivateMethod (MyClass *myClass, id anArg)
{
    fprintf (stderr, "MyClass (%d) was passed %p", myClass->test, anArg);
}

- (void) someMethod: (id) anArg
{
    somePrivateMethod (self, anArg);
}

@end

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X