Эта ветка довольно старая, и большая часть того, чем я хочу поделиться, уже здесь.
Тем не менее, мой любимый метод не упоминается, и AFAIK нет родной поддержки в текущем Clang, так что здесь я иду ...
Во-первых, и прежде всего (как уже отмечали другие) абстрактные классы являются чем-то необычным в Objective-C - мы обычно используем вместо этого композицию (иногда посредством делегирования). Вероятно, это причина того, что такая функция еще не существует в языке / компиляторе - кроме@dynamic
свойств, которые IIRC были добавлены в ObjC 2.0, сопровождающем введение CoreData.
Но учитывая, что (после тщательной оценки вашей ситуации!) Вы пришли к выводу, что делегирование (или состав в целом) не очень подходит для решения вашей проблемы, вот как я это делаю:
- Реализуйте каждый абстрактный метод в базовом классе.
- Сделать эту реализацию
[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()
может удивить людей, но и это достаточно легко понять.)
- Его нельзя удалить в сборках выпуска без генерации других предупреждений компилятора или ошибок - в отличие от подхода, основанного на одном из макросов утверждений.
Этот последний пункт требует пояснения, я думаю:
Некоторые (большинство?) Люди отбрасывают утверждения в сборках релизов. (Я не согласен с этой привычкой, но это другая история ...) Неспособность реализовать требуемый метод - однако - это плохо , ужасно , неправильно и, по сути, конец вселенной для вашей программы. Ваша программа не может работать правильно в этом отношении, потому что она не определена, а неопределенное поведение - худшая вещь в мире. Следовательно, возможность лишить эту диагностику без создания новой диагностики будет совершенно неприемлемо.
Достаточно плохо, что вы не можете получить надлежащую диагностику во время компиляции для таких ошибок программиста, и вынуждены прибегать к обнаружению во время выполнения для них, но если вы можете обмазать это в сборках релиза, зачем пытаться иметь абстрактный класс в первое место?