512 Stimmen

Erstellen einer abstrakten Klasse in Objective-C

Ich bin ursprünglich ein Java-Programmierer, der jetzt mit Objective-C arbeitet. Ich würde gerne eine abstrakte Klasse erstellen, aber das scheint in Objective-C nicht möglich zu sein. Ist das möglich?

Wenn nicht, wie nahe kann ich in Objective-C an eine abstrakte Klasse herankommen?

635voto

Barry Wark Punkte 106328

Typischerweise sind Objective-C-Klassen nur per Konvention abstrakt - wenn der Autor eine Klasse als abstrakt dokumentiert, verwenden Sie sie einfach nicht, ohne sie zu subklassifizieren. Es gibt jedoch keine Kompilierzeit-Erzwingung, die die Instanziierung einer abstrakten Klasse verhindert. Tatsächlich gibt es nichts, was einen Benutzer daran hindert, Implementierungen abstrakter Methoden über eine Kategorie (d.h. zur Laufzeit) bereitzustellen. Sie können einen Benutzer dazu zwingen, zumindest bestimmte Methoden zu überschreiben, indem Sie eine Ausnahme in der Implementierung dieser Methoden in Ihrer abstrakten Klasse auslösen:

[NSException raise:NSInternalInconsistencyException 
            format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];

Wenn Ihre Methode einen Wert zurückgibt, ist es ein bisschen einfacher, die

@throw [NSException exceptionWithName:NSInternalInconsistencyException
                               reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
                             userInfo:nil];

da Sie dann keine Rückgabeanweisung für die Methode hinzufügen müssen.

Wenn die abstrakte Klasse wirklich eine Schnittstelle ist (d. h. keine konkreten Methodenimplementierungen hat), ist die Verwendung eines Objective-C-Protokolls die geeignetere Option.

265voto

Grouchal Punkte 9729

Nein, es gibt keine Möglichkeit, eine abstrakte Klasse in Objective-C zu erstellen.

Sie können eine abstrakte Klasse nachahmen, indem Sie die Methoden/Selektoren doesNotRecognizeSelector: aufrufen lassen und damit eine Ausnahme auslösen, die die Klasse unbrauchbar macht.

Zum Beispiel:

- (id)someMethod:(SomeObject*)blah
{
     [self doesNotRecognizeSelector:_cmd];
     return nil;
}

Sie können dies auch für init tun.

60voto

Dan Rosenstark Punkte 66285

Nur Riffing auf @Barry Wark Antwort oben (und die Aktualisierung für iOS 4.3) und lassen Sie diese für meine eigene Referenz:

#define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]
#define methodNotImplemented() mustOverride()

dann können Sie in Ihren Methoden Folgendes verwenden

- (void) someMethod {
     mustOverride(); // or methodNotImplemented(), same thing
}

Anmerkungen: Ich bin mir nicht sicher, ob es eine gute Idee ist, ein Makro wie eine C-Funktion aussehen zu lassen, aber ich werde es beibehalten, bis ich eines Besseren belehrt werde. Ich denke, es ist korrekter, Folgendes zu verwenden NSInvalidArgumentException (anstelle von NSInternalInconsistencyException ), denn das ist es, was das Laufzeitsystem als Antwort auf doesNotRecognizeSelector aufgerufen wird (siehe NSObject docs).

43voto

redfood Punkte 659

Die Lösung, die ich gefunden habe, lautet:

  1. Erstellen Sie ein Protokoll für alles, was Sie in Ihrer "abstrakten" Klasse haben wollen
  2. Erstellen Sie eine Basisklasse (oder nennen Sie sie vielleicht abstrakt), die das Protokoll implementiert. Implementieren Sie alle Methoden, die Sie "abstrakt" haben wollen, in der .m-Datei, aber nicht in der .h-Datei.
  3. Lassen Sie Ihre Kindklasse von der Basisklasse erben UND das Protokoll implementieren.

Auf diese Weise wird der Compiler eine Warnung für jede Methode im Protokoll ausgeben, die nicht von Ihrer Kindklasse implementiert wird.

Es ist nicht so prägnant wie in Java, aber Sie erhalten die gewünschte Compilerwarnung.

35voto

Von der Mailingliste der Omni-Gruppe :

Objective-C hat nicht das abstrakte Compiler-Konstrukt wie Java unter dieser Zeit.

Sie definieren die abstrakte Klasse also einfach wie jede andere normale Klasse und implementieren Methoden-Stubs für die abstrakten Methoden, die entweder leer sind leer sind oder keine Unterstützung für den Selektor melden. Zum Beispiel...

- (id)someMethod:(SomeObject*)blah
{
     [self doesNotRecognizeSelector:_cmd];
     return nil;
}

Ich tue auch das Folgende, um die Initialisierung der abstrakten Klasse über den Standardinitialisierer zu verhindern.

- (id)init
{
     [self doesNotRecognizeSelector:_cmd];
     [self release];
     return nil;
}

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