Эта тема довольно старая, и большая часть того, чем я хочу поделиться, уже здесь.
Тем не менее, мой любимый метод не упомянут, и у AFAIK нет никакой нативной поддержки в текущем Clang, так что я иду…
Во-первых, и прежде всего (как уже отмечали другие) абстрактные классы являются чем-то необычным в Objective-C - вместо этого мы обычно используем композицию (иногда посредством делегирования). Вероятно, это причина того, что такая функция еще не существует в языке / компиляторе - кроме свойств @dynamic
, которые IIRC были добавлены в ObjC 2.0, сопровождающую введение CoreData.
Но учитывая, что (после тщательной оценки вашей ситуации!) Вы пришли к выводу, что делегирование (или состав в целом) не очень подходит для решения вашей проблемы, вот как I делает это :
- Реализуйте каждый абстрактный метод в базовом классе.
- Сделать эту реализацию
[self doesNotRecognizeSelector:_cmd];
…
- … сопровождаемый
__builtin_unreachable();
, чтобы заставить замолчать предупреждение, которое вы получите для не пустых методов, сообщая вам «контроль достигнут конец не пустой функции без возврата».
- Либо объедините шаги 2. и 3. в макросе, либо аннотируйте
-[NSObject doesNotRecognizeSelector:]
, используя __attribute__((__noreturn__))
в категории без реализации , чтобы не заменить первоначальную реализацию этого метода, и включите заголовок для этой категории в PCH вашего проекта.
Я лично предпочитаю макро-версию, так как она позволяет максимально уменьшить шаблон.
Вот оно:
// 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
Как вы можете видеть, макрос обеспечивает полную реализацию абстрактных методов, сводя необходимое количество шаблонов к абсолютному минимуму.
Еще лучшим вариантом было бы лоббировать команду Clang для предоставления атрибута компилятора для этого случая через запросы функций. (Лучше, потому что это также позволило бы диагностику во время компиляции для тех сценариев, где вы подкласс, например, NSIncrementalStore.)
Почему я выбираю этот метод
- Это сделано эффективно и несколько удобно.
- Это довольно легко понять. (Хорошо, что
__builtin_unreachable()
может удивить людей, но и это достаточно легко понять.)
- Его нельзя удалить в сборках выпуска без генерации других предупреждений компилятора или ошибок - в отличие от подхода, основанного на одном из макросов утверждений.
Этот последний пункт требует некоторого объяснения, я думаю:
Некоторые (большинство?) Люди отбрасывают утверждения в сборках релизов. (Я не согласен с этой привычкой, но это другая история ...) Если не реализовать требуемый метод - однако - это плохо , ужасно , неправильно и в основном конец вселенной для вашей программы. Ваша программа не может работать правильно в этом отношении, потому что она не определена, а неопределенное поведение - худшая вещь в мире. Следовательно, возможность лишить эту диагностику без создания новой диагностики будет совершенно неприемлемо.
Достаточно плохо, что вы не можете получить надлежащую диагностику во время компиляции для таких ошибок программиста, и вынуждены прибегать к обнаружению во время выполнения для них, но если вы можете обмазать это в сборках релиза, зачем пытаться иметь абстрактный класс на первом месте?