Pergunta

Eu sou originalmente um programador Java que agora trabalha com Objective-C. Eu gostaria de criar uma classe abstrata, mas que não parece ser possível em Objective-C. Isso é possível?

Se não, como perto de uma classe abstrata posso entrar em Objective-C?

Foi útil?

Solução

Normalmente, classe Objective-C são abstratas por convenção somente se os documentos autor uma classe como abstrata, simplesmente não usá-lo sem subclasse-lo. Não há aplicação de tempo de compilação que impede instanciação de uma classe abstrata, no entanto. De facto, não há nada para impedir um utilizador de fornecimento de implementações de métodos abstract através de uma categoria (isto é, em tempo de execução). Você pode forçar um usuário a pelo menos substituem determinados métodos levantando uma exceção naqueles métodos de implementação em sua classe abstrata:

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

Se o método retorna um valor, que é um pouco mais fácil de usar

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

como então você não precisa adicionar uma instrução de retorno do método.

Se a classe abstracta é realmente uma interface (ou seja, não tem implementações do método concreto), utilizando um protocolo Objectivo-C é a opção mais apropriado.

Outras dicas

Não, não há nenhuma maneira de criar uma classe abstrata em Objective-C.

Você pode zombar uma classe abstrata - fazendo os métodos / seletores chamar doesNotRecognizeSelector: e, portanto, gerar uma exceção tornando a classe inutilizável

.

Por exemplo:

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

Você também pode fazer isso para o init.

Apenas riffing sobre a resposta de @Barry Wark acima (e atualizar para o iOS 4.3) e deixar isso para minha própria referência:

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

, em seguida, em seus métodos que você pode usar este

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


Notas: Não tenho certeza se fazer um olhar macro como uma função C é uma boa idéia ou não, mas vou mantê-lo até educado em contrário. Eu acho que é mais correto uso NSInvalidArgumentException (em vez de NSInternalInconsistencyException) uma vez que é o que o sistema runtime joga em resposta a doesNotRecognizeSelector sendo chamado (ver docs NSObject).

A solução que eu vim com é:

  1. Criar um protocolo para tudo o que você quer em sua "abstrato" class
  2. Crie uma classe base (ou talvez chamá-lo abstrato) que implementa o protocolo. Para todos os métodos que você quer "abstrato" implementá-las no arquivo .m, mas não o arquivo .h.
  3. Tenha sua classe herde criança da classe base e implementar o Protocolo.

Desta forma, o compilador vai lhe dar um aviso para qualquer método no protocolo que não é implementada por sua classe criança.

Não é tão sucinto quanto em Java, mas você começa o aviso do compilador desejado.

A partir da Omni Grupo :

Objective-C não tem a construção do compilador abstrato como Java no este tempo.

Então, tudo que você faz é definir a classe abstrata como qualquer outra classe normal, e implementar métodos stubs para os métodos abstratos que sejam ou esvaziar ou relatório não suporte para seletor. Por exemplo ...

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

Eu também faço o seguinte para evitar a inicialização do resumo classe via o inicializador padrão.

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

Em vez de tentar criar uma classe base abstrata, considere o uso de um protocolo (similar a uma interface Java). Isso permite que você defina um conjunto de métodos e aceite todos os objectos que estejam em conformidade com o protocolo e implementar os métodos. Por exemplo, eu posso definir um protocolo de operação e, em seguida, ter uma função como esta:

- (void)performOperation:(id<Operation>)op
{
   // do something with operation
}

Onde op pode ser qualquer objeto que implementa o protocolo de operação.

Se você precisa de sua classe base abstrata para fazer mais do que simplesmente definir métodos, você pode criar uma classe regular Objective-C e impedir que ele seja instanciado. Apenas substituir o - função init (id) e fazê-lo retornar nulo ou assert (false). Não é uma solução muito limpa, mas desde que Objective-C é totalmente dinâmico, não há realmente nenhuma equivalente direto a uma classe base abstrata.

Esta discussão é uma espécie de idade, e mais do que eu quero compartilhar já está aqui.

No entanto, o meu método favorito não é mencionado, e AFAIK não há nenhum suporte nativo na atual Clang, então aqui vou eu ...

Em primeiro lugar, e acima de tudo (como outros apontaram já) classes abstratas são algo muito incomum em Objective-C - geralmente usamos composição (por vezes através de delegação) em vez. Esta é provavelmente a razão pela qual esse recurso ainda não existir no idioma / compilador -. Além de propriedades @dynamic, que IIRC foram adicionados em ObjC 2.0 que acompanha a introdução de CoreData

Mas dado que (após uma avaliação cuidadosa da sua situação!) De ter chegado à conclusão de que a delegação (ou composição em geral) não é bem adequado para resolver o seu problema, é aqui como I fazer -lo:

  1. Implementar todos os métodos abstratos na classe base.
  2. Faça que a implementação [self doesNotRecognizeSelector:_cmd]; ...
  3. ... seguido por __builtin_unreachable(); para silenciar o aviso você vai ter para métodos não-vazios, dizendo que “o fim alcançado o controle da função não-vazio, sem um retorno”.
  4. Ou combinar as etapas 2 e 3. numa macro, ou anotar -[NSObject doesNotRecognizeSelector:] usando __attribute__((__noreturn__)) em uma categoria sem implementação de modo a não substituir a implementação original desse método, e incluem o cabeçalho para que categoria na PCH do seu projeto.

Eu pessoalmente prefiro a versão macro como que me permite reduzir o clichê, tanto quanto 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 plena implementação dos métodos abstratos, reduzindo a quantidade necessária de clichê a um mínimo absoluto.

Uma opção ainda melhor seria Clang equipe a fornecer um atributo de compilador para este caso, através de pedidos de recurso. (Melhor, porque isso também permitiria diagnósticos em tempo de compilação para esses cenários onde você subclasse por exemplo NSIncrementalStore.)

Por que escolher esse método

  1. começar o trabalho feito de forma eficiente, e um pouco conveniente.
  2. É bastante fácil de entender. (Ok, isso __builtin_unreachable() pode surpreender as pessoas, mas é bastante fácil de entender, também.)
  3. Não pode ser despojado em compilações sem gerar outros avisos do compilador, ou erros -. Ao contrário de uma abordagem que se baseia em uma das macros de declaração

Esse último ponto precisa de alguma explicação, eu acho:

Alguns (a maioria?) Pessoas afirmações tira em compilações. (Não concordo com esse hábito, mas isso é outra história ...) Na falta de implementar um método necessário - no entanto - é mau , terrível , errado , e basicamente o fim do universo para o seu programa. Seu programa não pode funcionar corretamente a este respeito porque é indefinido, e um comportamento indefinido é a pior coisa do mundo. Daí, ser capaz de retirar esses diagnósticos sem gerar novos diagnósticos seria completamente inaceitável.

É ruim o suficiente que você não pode obter diagnósticos em tempo de compilação apropriados para tais erros programador, e ter que recorrer a descoberta at-run-time para estes, mas se você pode gesso sobre ele em compilações, por que tentar ter um resumo classe em primeiro lugar?

Usando @property e @dynamic também poderia funcionar. Se você declarar uma propriedade dinâmica e não dar uma implementação método de correspondência, tudo ainda irá compilar sem avisos, e você obterá um erro unrecognized selector em tempo de execução se você tentar acessá-lo. Isto essencialmente a mesma coisa que chamar [self doesNotRecognizeSelector:_cmd], mas com muito menos digitação.

No Xcode (usando clang etc) Eu gosto de usar __attribute__((unavailable(...))) para marcar as classes abstratas de modo a obter um erro / aviso se você tentar usá-lo.

Ele fornece alguma proteção contra acidentalmente usando o método.

Exemplo

a tag classe base @interface os métodos "abstratos":

- (void)myAbstractMethod:(id)param1 __attribute__((unavailable("You should always override this")));

Tomar ainda mais este passo, eu criar uma macro:

#define UnavailableMacro(msg) __attribute__((unavailable(msg)))

Isso permite que você faça o seguinte:

- (void)myAbstractMethod:(id)param1 UnavailableMacro(@"You should always override this");

Como eu disse, isso não é proteção compilador real, mas é quase tão bom quanto o vai conseguir em um idioma que não suporta métodos abstratos.

A resposta para a pergunta é espalhados nos comentários sob as respostas já dadas. Então, eu estou apenas resumindo e simplificando aqui.

Opção 1: Protocolos

Se você quiser criar uma classe abstrata sem uso de implementação 'Protocolos'. As classes que herdam um protocolo são obrigados a implementar os métodos de protocolo.

@protocol ProtocolName
// list of methods and properties
@end

Option2: Template Method Padrão

Se você quiser criar uma classe abstrata com implementação parcial como "Pattern Template Method", então esta é a solução. Objective-C -? Template métodos padrão

Outra alternativa

Basta verificar a classe na classe abstrata e Assert ou de exceção, o que você quiser.

@implementation Orange
- (instancetype)init
{
    self = [super init];
    NSAssert([self class] != [Orange class], @"This is an abstract class");
    if (self) {
    }
    return self;
}
@end

Isso elimina a necessidade de substituir init

(mais de uma sugestão relacionada)

Eu queria ter uma maneira de deixar o know programador "não chamar de criança" e substituir completamente (no meu caso ainda oferecem alguma funcionalidade padrão em nome do pai quando não ampliado):

typedef void override_void;
typedef id override_id;

@implementation myBaseClass

// some limited default behavior (undesired by subclasses)
- (override_void) doSomething;
- (override_id) makeSomeObject;

// some internally required default behavior
- (void) doesSomethingImportant;

@end

A vantagem é que o programador vai ver o "override" na declaração e vai saber que eles não devem ser chamando [super ..].

Com certeza, é feio ter que definir tipos de retorno individuais para isso, mas serve como uma boa dica visual suficiente e você facilmente não pode usar a parte "override_" em uma definição de subclasse.

É claro que uma classe pode ainda ter uma implementação padrão quando a extensão é opcional. Mas, como as outras respostas dizer, implementar uma exceção de tempo de execução quando apropriado, como por classes abstratas (virtuais).

Seria bom ter construído em dicas de compilador como este, até mesmo dicas para quando é melhor para pré / pós chamada do super implementar, em vez de ter que cavar através de comentários / documentação ou ... supor.

exemplo da dica

Se você são utilizados para o compilador captura violações instanciação abstratas em outras línguas, então o comportamento Objective-C é decepcionante.

Como uma linguagem de ligação tardia é claro que Objective-C não pode tomar decisões estáticos sobre se uma classe realmente é abstrato ou não (você pode estar adicionando funções em tempo de execução ...), mas para casos de uso típicos este parece ser um deficiência. Eu preferiria o compilador flat-out instantiations impedido de classes abstratas em vez de jogar um erro em tempo de execução.

Aqui é um padrão que estamos usando para obter este tipo de verificação estática usando um par de técnicas para esconder initializers:

//
//  Base.h
#define UNAVAILABLE __attribute__((unavailable("Default initializer not available.")));

@protocol MyProtocol <NSObject>
-(void) dependentFunction;
@end

@interface Base : NSObject {
    @protected
    __weak id<MyProtocol> _protocolHelper; // Weak to prevent retain cycles!
}

- (instancetype) init UNAVAILABLE; // Prevent the user from calling this
- (void) doStuffUsingDependentFunction;
@end

//
//  Base.m
#import "Base.h"

// We know that Base has a hidden initializer method.
// Declare it here for readability.
@interface Base (Private)
- (instancetype)initFromDerived;
@end

@implementation Base
- (instancetype)initFromDerived {
    // It is unlikely that this becomes incorrect, but assert
    // just in case.
    NSAssert(![self isMemberOfClass:[Base class]],
             @"To be called only from derived classes!");
    self = [super init];
    return self;
}

- (void) doStuffUsingDependentFunction {
    [_protocolHelper dependentFunction]; // Use it
}
@end

//
//  Derived.h
#import "Base.h"

@interface Derived : Base
-(instancetype) initDerived; // We cannot use init here :(
@end

//
//  Derived.m
#import "Derived.h"

// We know that Base has a hidden initializer method.
// Declare it here.
@interface Base (Private)
- (instancetype) initFromDerived;
@end

// Privately inherit protocol
@interface Derived () <MyProtocol>
@end

@implementation Derived
-(instancetype) initDerived {
    self= [super initFromDerived];
    if (self) {
        self->_protocolHelper= self;
    }
    return self;
}

// Implement the missing function
-(void)dependentFunction {
}
@end

Provavelmente este tipo de situações só deve acontecer no tempo de desenvolvimento, de modo que este trabalho poderá:

- (id)myMethodWithVar:(id)var {
   NSAssert(NO, @"You most override myMethodWithVar:");
   return nil;
}

Você pode usar um método proposto por @Yar (com algumas modificações):

#define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]
#define setMustOverride() NSLog(@"%@ - method not implemented", NSStringFromClass([self class])); mustOverride()

Aqui você vai receber uma mensagem como:

<Date> ProjectName[7921:1967092] <Class where method not implemented> - method not implemented
<Date> ProjectName[7921:1967092] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[<Base class (if inherited or same if not> <Method name>] must be overridden in a subclass/category'

Ou afirmação:

NSAssert(![self respondsToSelector:@selector(<MethodName>)], @"Not implemented");

Neste caso, você vai ter:

<Date> ProjectName[7926:1967491] *** Assertion failure in -[<Class Name> <Method name>], /Users/kirill/Documents/Projects/root/<ProjectName> Services/Classes/ViewControllers/YourClass:53

Além disso, você pode usar protocolos e outras soluções -. Mas este é um dos mais simples

Cacau não fornece qualquer coisa chamada abstrato. Podemos criar uma classe abstrata que obtém verificado apenas em tempo de execução, e em tempo de compilação não está marcada.

Eu normalmente apenas desactivar o método init em uma classe que eu quero resumo:

- (instancetype)__unavailable init; // This is an abstract class.

Isso irá gerar um erro em tempo de compilação sempre que você chamar o init nessa classe. Eu, então, usar métodos de classe para tudo o resto.

Objective-C não tem built-in maneira para declarar classes abstratas.

Mudando um pouco o que @redfood sugeriu aplicando @ comentário de dotToString, você realmente tem a solução adoptada pelo Instagram do IGListKit .

  1. Criar um protocolo para todos os métodos que não fazem sentido para ser definida na base (abstrata) classe ou seja, eles precisam de implementações específicas nas crianças.
  2. Criar uma base de classe (resumo) que não implementar este protocolo. Você pode adicionar a esta classe quaisquer outros métodos que fazem sentido para ter uma implementação comum.
  3. Em todos os lugares em seu projeto, se uma criança de AbstractClass deve ser de entrada ou de saída por algum método, digite-o como AbstractClass<Protocol> vez.

Porque AbstractClass não implementa Protocol, a única maneira de ter uma instância AbstractClass<Protocol> é de subclassificação. Como AbstractClass sozinho não pode estar em qualquer lugar utilizado no projeto, torna-se abstrata.

Claro, isso não impede que os desenvolvedores desavisadas da adição de novos métodos referentes simplesmente para AbstractClass, que acabaria por permitir que uma instância da (não mais) classe abstrata.

exemplo do mundo real: IGListKit tem uma IGListSectionController classe base que não implementa a IGListSectionType protocolo, no entanto cada método que requer uma instância da classe que, na verdade, pede o tipo IGListSectionController<IGListSectionType>. Portanto não há nenhuma maneira de usar um objeto do tipo IGListSectionController para qualquer coisa útil em seu quadro.

Na verdade, Objective-C não tem classes abstratas, mas você pode usar Protocolos para alcançar o mesmo efeito. Aqui está o exemplo:

CustomProtocol.h

#import <Foundation/Foundation.h>

@protocol CustomProtocol <NSObject>
@required
- (void)methodA;
@optional
- (void)methodB;
@end

TestProtocol.h

#import <Foundation/Foundation.h>
#import "CustomProtocol.h"

@interface TestProtocol : NSObject <CustomProtocol>

@end

TestProtocol.m

#import "TestProtocol.h"

@implementation TestProtocol

- (void)methodA
{
  NSLog(@"methodA...");
}

- (void)methodB
{
  NSLog(@"methodB...");
}
@end

Um exemplo simples de criar uma classe abstrata

// Declare a protocol
@protocol AbcProtocol <NSObject>

-(void)fnOne;
-(void)fnTwo;

@optional

-(void)fnThree;

@end

// Abstract class
@interface AbstractAbc : NSObject<AbcProtocol>

@end

@implementation AbstractAbc

-(id)init{
    self = [super init];
    if (self) {
    }
    return self;
}

-(void)fnOne{
// Code
}

-(void)fnTwo{
// Code
}

@end

// Implementation class
@interface ImpAbc : AbstractAbc

@end

@implementation ImpAbc

-(id)init{
    self = [super init];
    if (self) {
    }
    return self;
}

// You may override it    
-(void)fnOne{
// Code
}
// You may override it
-(void)fnTwo{
// Code
}

-(void)fnThree{
// Code
}

@end

Você não pode simplesmente criar um delegado?

Um delegado é como uma classe base abstrata no sentido de que você diz que funções precisam ser definidos, mas você realmente não defini-los.

Em seguida, sempre que você implementar o delegado (ou seja classe abstrata) você será avisado pelo compilador do que opcional e funções obrigatórias que você precisa para definir o comportamento de.

Isso soa como uma classe base abstrata para mim.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top