Esse tópico é meio antigo, e a maior parte do que eu quero compartilhar já está aqui.
No entanto, meu método favorito não é mencionado, e o AFAIK não possui suporte nativo no Clang atual, então aqui vou eu…
Primeiro, e principalmente (como outros já apontaram) as classes abstratas são algo muito incomum no Objective-C - geralmente usamos composição (às vezes por meio de delegação). Essa é provavelmente a razão pela qual esse recurso ainda não existe no idioma / compilador - além das @dynamic
propriedades, que o IIRC foi adicionado no ObjC 2.0 que acompanha a introdução do CoreData.
Mas, considerando que (após uma avaliação cuidadosa da sua situação!), Você chegou à conclusão de que delegação (ou composição em geral) não é adequada para resolver seu problema, eis como eu faço isso:
- Implemente todos os métodos abstratos na classe base.
- Faça essa implementação
[self doesNotRecognizeSelector:_cmd];
...
- … Seguido por
__builtin_unreachable();
silenciar o aviso que você receberá por métodos não nulos, informando que “o controle atingiu o final da função não nula sem retorno”.
- Combine as etapas 2. e 3. em uma macro ou anote o
-[NSObject doesNotRecognizeSelector:]
uso __attribute__((__noreturn__))
em uma categoria sem implementação para não substituir a implementação original desse método e inclua o cabeçalho dessa categoria na PCH do seu projeto.
Pessoalmente, eu prefiro a versão macro, pois isso permite que eu reduza o padrão conforme possível.
Aqui está:
// 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
Como você pode ver, a macro fornece a implementação completa dos métodos abstratos, reduzindo a quantidade necessária de clichê para um mínimo absoluto.
Uma opção ainda melhor seria pressionar a equipe Clang para fornecer um atributo de compilador para este caso, por meio de solicitações de recursos. (Melhor, porque isso também permitiria diagnósticos em tempo de compilação para os cenários em que você subclasse, por exemplo, NSIncrementalStore.)
Por que eu escolhi este método
- O trabalho é realizado de maneira eficiente e conveniente.
- É bastante fácil de entender. (Ok, isso
__builtin_unreachable()
pode surpreender as pessoas, mas também é fácil de entender.)
- Ele não pode ser removido nas compilações de versão sem gerar outros avisos ou erros do compilador - ao contrário de uma abordagem baseada em uma das macros de asserção.
Esse último ponto precisa de uma explicação, eu acho:
Algumas pessoas (a maioria?) Retiram as asserções nas versões de lançamento. (Não concordo com esse hábito, mas isso é outra história ...) Falhar na implementação de um método necessário - no entanto - é ruim , terrível , errado e basicamente o fim do universo para o seu programa. Seu programa não pode funcionar corretamente nesse sentido porque é indefinido e o comportamento indefinido é a pior coisa de todos os tempos. Portanto, ser capaz de eliminar esses diagnósticos sem gerar novos diagnósticos seria completamente inaceitável.
Já é ruim o suficiente que você não possa obter diagnósticos em tempo de compilação adequados para esses erros de programador, e tenha que recorrer à descoberta em tempo de execução para esses erros, mas se você pode aplicar isso em versões de compilação, por que tentar ter uma classe abstrata no primeiro lugar?