web-dev-qa-db-de.com

Beste Möglichkeit, private Methoden für eine Klasse in Objective-C zu definieren

Ich habe gerade mit der Programmierung von Objective-C begonnen und mich gefragt, wie Leute, die Objective-C-Programme schreiben, mit privaten Methoden umgehen.

Ich verstehe, dass es möglicherweise mehrere Konventionen und Gewohnheiten gibt, und denke über diese Frage nach, als ein Aggregator der besten Techniken, die Menschen verwenden, um mit privaten Methoden in Objective-C umzugehen.

Bitte geben Sie bei der Veröffentlichung ein Argument für Ihre Vorgehensweise an. Warum ist es gut? Welche Nachteile hat es (die Sie kennen) und wie gehen Sie damit um?


Was meine bisherigen Ergebnisse betrifft.

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

Dieser Ansatz hat zwei Probleme:

  1. Xcode (und Compiler?) Überprüft nicht, ob Sie alle Methoden in der privaten Kategorie im entsprechenden @ Implementierungsblock definieren
  2. Sie müssen @interface, das Ihre private Kategorie deklariert, an den Anfang der MyClass.m-Datei setzen, da sich Xcode ansonsten mit einer Nachricht wie "self reagiert möglicherweise nicht auf die Nachricht" privateFoo "beschwert.

Das erste Problem kann mit leere Kategorie [z. Meine Klasse ()].
Der zweite stört mich sehr. Ich würde gerne sehen, wie private Methoden am Ende der Datei implementiert (und definiert) werden. Ich weiß nicht, ob das möglich ist.

350
Yurii Soldak

Wie andere bereits gesagt haben, gibt es in Objective-C keine private Methode. Ab Objective-C 2.0 (dh Mac OS X Leopard, iPhone OS 2.0 und höher) können Sie jedoch eine Kategorie mit einem leeren Namen (dh @interface MyClass ()) namens Class Extension erstellen =. Das Besondere an einer Klassenerweiterung ist, dass die Methodenimplementierungen im selben @implementation MyClass als öffentliche Methode. Also strukturiere ich meine Klassen so:

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, nicht nach der (manchmal willkürlichen) öffentlichen/privaten Unterscheidung.

433
Alex

Es gibt keine "private Methode" in Objective-C, wenn die Laufzeitumgebung herausfinden kann, welche Implementierung sie verwenden soll. Das heißt aber nicht, dass es keine Methoden gibt, die nicht Teil der dokumentierten Schnittstelle sind. Für diese Methoden denke ich, dass eine Kategorie in Ordnung ist. Anstatt das @interface Wie bei Punkt 2 oben in die .m-Datei einzufügen, würde ich es in eine eigene .h-Datei einfügen. Eine Konvention, der ich folge (und die ich an anderer Stelle gesehen habe, glaube ich, dass es sich um eine Apple Konvention handelt, da Xcode sie jetzt automatisch unterstützt)), besteht darin, eine solche Datei nach ihrer Klasse und Kategorie mit einem + zu benennen , also @interface GLObject (PrivateMethods) kann in GLObject+PrivateMethods.h gefunden werden. Der Grund für die Angabe der Header-Datei ist, dass Sie sie in Ihre Unit-Test-Klassen importieren können :-).

Übrigens: Was die Implementierung/Definition von Methoden am Ende der .m-Datei betrifft, können Sie dies 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 der Implementierung in beliebiger Reihenfolge definiert und verwendet werden. Sie müssen also unbedingt die "privaten" Methoden am Ende der Datei einfügen.

Selbst mit Klassenerweiterungen erstelle ich häufig einen separaten Header (GLObject+Extension.h), Damit ich diese Methoden bei Bedarf verwenden kann und die Sichtbarkeit von "friend" oder "protected" imitiere.

Da diese Antwort ursprünglich geschrieben wurde, hat der Clang-Compiler damit begonnen, zwei Durchgänge für Objective-C-Methoden durchzuführen. Dies bedeutet, dass Sie vermeiden können, Ihre "privaten" Methoden vollständig zu deklarieren, und ob sie sich über oder unter der aufrufenden Site befinden, werden sie vom Compiler gefunden.

50
user23743

Obwohl ich kein Objective-C-Experte bin, definiere ich persönlich nur die Methode bei der Implementierung meiner Klasse. Zugegeben, es muss vor (oben) allen Methoden definiert werden, die es aufrufen, aber es erfordert definitiv den geringsten Arbeitsaufwand.

37
Andy

Die Definition Ihrer privaten Methoden im Block @implementation Ist für die meisten Zwecke ideal. Clang wird diese unabhängig von der Deklarationsreihenfolge im @implementation Anzeigen. Es ist nicht erforderlich, sie in einer Klassenfortsetzung (auch als Klassenerweiterung bezeichnet) oder in einer 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 @implementation Verwenden).

static -Funktionen eignen sich sehr gut für besonders sensible oder geschwindigkeitskritische private Methoden.

Eine Konvention zum Benennen von Präfixen kann Ihnen dabei helfen, zu vermeiden, dass versehentlich private Methoden überschrieben werden (ich finde den Klassennamen als Präfix sicher).

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

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

Hier ist ein kleines kommentiertes 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 möglicherweise nicht offensichtlich ist: Ein C++ - Typ kann sowohl sehr schnell sein als auch ein viel höheres Maß an Kontrolle bieten und gleichzeitig die Anzahl der exportierten und geladenen objc-Methoden minimieren.

19
justin

Sie können versuchen, eine statische Funktion unterhalb oder oberhalb Ihrer Implementierung zu definieren, die einen Zeiger auf Ihre Instanz verwendet. Es wird in der Lage sein, auf jede Ihrer Instanzvariablen zuzugreifen.

//.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
14
dreamlax

Sie könnten Blöcke verwenden?

@implementation MyClass

id (^createTheObject)() = ^(){ return [[NSObject alloc] init];};

NSInteger (^addEm)(NSInteger, NSInteger) =
^(NSInteger a, NSInteger b)
{
    return a + b;
};

//public methods, etc.

- (NSObject) thePublicOne
{
    return createTheObject();
}

@end

Mir ist bewusst, dass dies eine alte Frage ist, aber es ist eine der ersten, die ich gefunden habe, als ich nach einer Antwort auf genau diese Frage gesucht habe. Ich habe diese Lösung nirgendwo anders erörtert. Lassen Sie mich wissen, ob dies dumm ist.

3
FellowMD

alle Objekte in Objective C entsprechen dem NSObject-Protokoll, das die performSelector: -Methode enthält. Zuvor suchte ich auch nach einer Möglichkeit, einige "helferhafte oder private" Methoden zu erstellen, die ich auf öffentlicher Ebene nicht benötigte. Wenn Sie eine private Methode ohne Overhead erstellen möchten und diese nicht in Ihrer Header-Datei definieren müssen, geben Sie diesem einen Versuch.

definieren Sie Ihre Methode mit einer ähnlichen Signatur wie im folgenden Code ...

-(void)myHelperMethod: (id) sender{
     // code here...
}

wenn Sie die Methode referenzieren müssen, rufen Sie sie einfach als Selektor auf.

[self performSelector:@selector(myHelperMethod:)];

diese Codezeile ruft die von Ihnen erstellte Methode auf und zeigt keine nervige Warnung an, dass sie nicht in der Header-Datei definiert ist.

3
Zack Sheppard

Wenn Sie den Block @interface Oben vermeiden wollten, konnten Sie die privaten Deklarationen immer in einer anderen Datei ablegen MyClassPrivate.h, Was nicht ideal ist, aber die Implementierung nicht überfrachtet.

MyClass.h

interface MyClass : NSObject {
 @private
  BOOL publicIvar_;
  BOOL privateIvar_;
}

@property (nonatomic, assign) BOOL publicIvar;
//any other public methods. etc
@end

MyClassPrivate.h

@interface MyClass ()

@property (nonatomic, assign) BOOL privateIvar;
//any other private methods etc.
@end

MyClass.m

#import "MyClass.h"
#import "MyClassPrivate.h"
@implementation MyClass

@synthesize privateIvar = privateIvar_;
@synthesize publicIvar = publicIvar_;

@end
2
rebelzach

Eine weitere Sache, die ich hier nicht erwähnt habe - Xcode unterstützt .h-Dateien mit "_private" im Namen. Angenommen, Sie haben eine Klasse MyClass - Sie haben MyClass.m und MyClass.h und jetzt können Sie auch MyClass_private.h haben. Xcode erkennt dies und nimmt es in die Liste der "Gegenstücke" im Assistenten-Editor auf.

//MyClass.m
#import "MyClass.h"
#import "MyClass_private.h"
2
Rich Schonthal

Es gibt keine Möglichkeit, Problem Nr. 2 zu umgehen. So funktioniert der C-Compiler (und damit auch der Objective-C-Compiler). Wenn Sie den XCode-Editor verwenden, sollte das Funktions-Popup das Navigieren in den Blöcken @interface Und @implementation In der Datei erleichtern.

1
Barry Wark

Es ist ein Vorteil der Abwesenheit von privaten Methoden. Sie können die Logik, die Sie ausblenden möchten, in die separate Klasse verschieben und als Stellvertreter verwenden. In diesem Fall können Sie das delegierte Objekt als privat markieren und es ist von außen nicht sichtbar. Durch Verschieben der Logik in die separate Klasse (möglicherweise mehrere) können Sie Ihr Projekt besser gestalten. Weil Ihre Klassen einfacher werden und Ihre Methoden in Klassen mit Eigennamen gruppiert werden.

1
Sneg

Wie andere Leute sagten, ist es für die meisten Zwecke in Ordnung, private Methoden im @implementation - Block zu definieren.

Zum Thema Codeorganisation - Ich halte sie gerne unter pragma mark private Zusammen, um die Navigation in Xcode zu vereinfachen

@implementation MyClass 
// .. public methods

# pragma mark private 
// ...

@end
0
Milan