Dieser Thread ist ziemlich alt und das meiste, was ich teilen möchte, ist bereits hier.
Meine Lieblingsmethode wird jedoch nicht erwähnt, und AFAIK gibt es im aktuellen Clang keine native Unterstützung, also los geht's…
In erster Linie (wie andere bereits betont haben) sind abstrakte Klassen in Objective-C etwas sehr Ungewöhnliches - wir verwenden stattdessen normalerweise Komposition (manchmal durch Delegation). Dies ist wahrscheinlich der Grund, warum eine solche Funktion in der Sprache / im Compiler noch nicht vorhanden ist - abgesehen von @dynamic
Eigenschaften, die IIRC in ObjC 2.0 zur Einführung von CoreData hinzugefügt wurden.
Angesichts der Tatsache, dass Sie (nach sorgfältiger Beurteilung Ihrer Situation!) Zu dem Schluss gekommen sind, dass die Delegation (oder die Zusammensetzung im Allgemeinen) nicht gut zur Lösung Ihres Problems geeignet ist, gehe ich folgendermaßen vor :
- Implementieren Sie jede abstrakte Methode in der Basisklasse.
- Machen Sie diese Implementierung
[self doesNotRecognizeSelector:_cmd];
...
- … Gefolgt von
__builtin_unreachable();
der Stummschaltung der Warnung, die Sie für nicht leere Methoden erhalten, und der Meldung, dass die Kontrolle das Ende der nicht leeren Funktion ohne Rückkehr erreicht hat.
- Kombinieren Sie entweder die Schritte 2. und 3. in einem Makro oder kommentieren Sie die
-[NSObject doesNotRecognizeSelector:]
Verwendung __attribute__((__noreturn__))
in einer Kategorie ohne Implementierung , um die ursprüngliche Implementierung dieser Methode nicht zu ersetzen, und fügen Sie den Header für diese Kategorie in den PCH Ihres Projekts ein.
Ich persönlich bevorzuge die Makroversion, da ich so die Kesselplatte so weit wie möglich reduzieren kann.
Hier ist es:
// Definition:
#define D12_ABSTRACT_METHOD {\
[self doesNotRecognizeSelector:_cmd]; \
__builtin_unreachable(); \
}
// Usage (assuming we were Apple, implementing the abstract base class NSString):
@implementation NSString
#pragma mark - Abstract Primitives
- (unichar)characterAtIndex:(NSUInteger)index D12_ABSTRACT_METHOD
- (NSUInteger)length D12_ABSTRACT_METHOD
- (void)getCharacters:(unichar *)buffer range:(NSRange)aRange D12_ABSTRACT_METHOD
#pragma mark - Concrete Methods
- (NSString *)substringWithRange:(NSRange)aRange
{
if (aRange.location + aRange.length >= [self length])
[NSException raise:NSInvalidArgumentException format:@"Range %@ exceeds the length of %@ (%lu)", NSStringFromRange(aRange), [super description], (unsigned long)[self length]];
unichar *buffer = (unichar *)malloc(aRange.length * sizeof(unichar));
[self getCharacters:buffer range:aRange];
return [[[NSString alloc] initWithCharactersNoCopy:buffer length:aRange.length freeWhenDone:YES] autorelease];
}
// and so forth…
@end
Wie Sie sehen können, bietet das Makro die vollständige Implementierung der abstrakten Methoden, wodurch die erforderliche Menge an Boilerplate auf ein absolutes Minimum reduziert wird.
Eine noch bessere Option wäre es, das Clang-Team dazu zu bewegen, über Feature-Anfragen ein Compiler-Attribut für diesen Fall bereitzustellen. (Besser, da dies auch die Diagnose zur Kompilierungszeit für Szenarien ermöglichen würde, in denen Sie eine Unterklasse haben, z. B. NSIncrementalStore.)
Warum ich diese Methode wähle
- Es erledigt die Arbeit effizient und etwas bequemer.
- Es ist ziemlich leicht zu verstehen. (Okay, das
__builtin_unreachable()
mag die Leute überraschen, aber es ist auch leicht zu verstehen.)
- In Release-Builds kann es nicht entfernt werden, ohne dass andere Compiler-Warnungen oder -Fehler generiert werden - im Gegensatz zu einem Ansatz, der auf einem der Assertion-Makros basiert.
Dieser letzte Punkt bedarf einer Erklärung, denke ich:
Einige (die meisten?) Leute entfernen Behauptungen in Release-Builds. (Ich bin mit dieser Angewohnheit nicht einverstanden, aber das ist eine andere Geschichte…) Die Nichtimplementierung einer erforderlichen Methode ist jedoch schlecht , schrecklich , falsch und im Grunde das Ende des Universums für Ihr Programm. Ihr Programm kann in dieser Hinsicht nicht richtig funktionieren, da es undefiniert ist und undefiniertes Verhalten das Schlimmste ist, was es je gab. Daher wäre es völlig inakzeptabel, diese Diagnosen entfernen zu können, ohne neue Diagnosen zu generieren.
Es ist schon schlimm genug, dass Sie für solche Programmiererfehler keine ordnungsgemäße Diagnose zur Kompilierungszeit erhalten und für diese Fehler zur Laufzeit auf diese zurückgreifen müssen. Wenn Sie sie jedoch in Release-Builds verputzen können, warum sollten Sie versuchen, eine abstrakte Klasse in der zu haben? erster Platz?