Métodos protegidos em Objective-C

112

Qual é o equivalente a métodos protegidos em Objective-C? Eu quero definir métodos que apenas as classes derivadas podem chamar / implementar.

LK.
fonte

Respostas:

47

Você não pode declarar um método protegido ou privado. A natureza dinâmica do Objective-C torna impossível implementar controles de acesso para métodos. (Você poderia fazer isso modificando fortemente o compilador ou o tempo de execução, com uma grande penalidade de velocidade, mas por razões óbvias isso não é feito.)

Retirado da fonte .

Sachin Shanbhag
fonte
Embora tecnicamente você não possa, você pode emular variáveis ​​privadas.
Sharen Eayrs
Lee - se você declarar um ponteiro de função em @protected e atribuir uma função no método init, isso funcionaria?
bikram990
156

Você pode simular o acesso protegido e privado a métodos fazendo o seguinte:

  • Declare seus métodos privados em uma extensão de classe (ou seja, uma categoria sem nome declarada perto do topo do arquivo .m da classe)
  • Declare seus métodos protegidos em um cabeçalho de subclasse - a Apple usa esse padrão em relação ao UIGestureRecognizer (consulte a documentação e a referência a UIGestureRecognizerSubclass.h)

Essas proteções não são, como observou Sachin, aplicadas em tempo de execução (como no Java, por exemplo).

Brian Westphal
fonte
2
Sobre a solução do tipo UIGestureRecognizer: O problema é que se algum código importar a subclasse, ele também importará o cabeçalho da Subclasse e, portanto, terá acesso aos métodos "protegidos". Existe uma maneira de contornar isso?
yonix de
5
Olá yonix, a importação do cabeçalho da subclasse seria feita dentro do arquivo .m e não dentro do arquivo .h, portanto, importar a subclasse não importaria esses métodos protegidos.
Brian Westphal
Sugestão legal Brian, muito obrigado !! Para o autor original, para propriedades declaradas, apenas certifique-se de usar @dynamic na implementação da extensão da classe da subclasse (categoria não nomeada), de modo que, em tempo de execução, a implementação da classe pai seja usada
usuário1046037
1
Como vejo UIGestureRecognizerSubclass.h?
Sharen Eayrs
5
Obrigado por apontar como a Apple faz isso internamente. Eu postou um exemplo completo de como implementar coisas da mesma maneira Apple faz emUIGestureRecognizerSubclass.h
sujo Henry
14

Aqui está o que fiz para tornar os métodos protegidos visíveis para minhas subclasses, sem exigir que eles implementem os métodos. Isso significa que não recebi avisos do compilador em minha subclasse sobre uma implementação incompleta.

SuperClassProtectedMethods.h (arquivo de protocolo):

@protocol SuperClassProtectedMethods <NSObject>
- (void) protectMethod:(NSObject *)foo;
@end

@interface SuperClass (ProtectedMethods) < SuperClassProtectedMethods >
@end

SuperClass.m: (o compilador agora o forçará a adicionar métodos protegidos)

#import "SuperClassProtectedMethods.h"
@implementation SuperClass
- (void) protectedMethod:(NSObject *)foo {}
@end

SubClass.m:

#import "SuperClassProtectedMethods.h"
// Subclass can now call the protected methods, but no external classes importing .h files will be able to see the protected methods.
Michael Kernahan
fonte
2
O significado de protegido é que não pode ser chamado externamente. Você ainda pode chamar quaisquer métodos definidos na classe, independentemente de serem visíveis externamente ou não.
eonil de
Sim, eu entendo isso. Este método funciona para o cérebro humano, não para o código compilado real. Mas Objective-C não permite isso (não ser capaz de chamar externamente). Você sempre pode fazer performSelectorisso.
Michael Kernahan
1
Você também pode fazer [(id)obj hiddenMethod]. Dizendo com precisão, o método protegido não é compatível com Objective-C.
eonil
O problema com isso é que suas chamadas classes protegidas não podem anunciar propriedades. Se você não precisa de propriedades, então qualquer pessoa sabe que você pode simplesmente adicionar categorias protegidas.
Sharen Eayrs
@eonil: "Você também pode fazer [(id) obj hiddenMethod]." Sim, você pode fazer isso, mas receberá um aviso do compilador, se esse método não estiver em nenhuma interface incluída.
Kaiserludi
9

Acabei de descobrir isso e funciona para mim. Para melhorar a resposta de Adam, em sua superclasse, faça uma implementação do método protegido no arquivo .m, mas não o declare no arquivo .h. Em sua subclasse, crie uma nova categoria em seu arquivo .m com a declaração do método protegido da superclasse e você pode usar o método protegido da superclasse em sua subclasse. Isso não impedirá que o chamador do método supostamente protegido seja forçado no tempo de execução.

/////// SuperClass.h
@interface SuperClass

@end

/////// SuperClass.m
@implementation SuperClass
- (void) protectedMethod
{}
@end

/////// SubClass.h
@interface SubClass : SuperClass
@end

/////// SubClass.m
@interface SubClass (Protected)
- (void) protectedMethod ;
@end

@implementation SubClass
- (void) callerOfProtectedMethod
{
  [self protectedMethod] ; // this will not generate warning
} 
@end
Redwud
fonte
2
Neste caso, o compilador ainda lança um aviso sobre o método não implementadoprotectedMethod
skywinder
Esta é uma boa solução, mas em vez de criar uma categoria (Protegido), você pode fazer uma extensão.
Dharmesh Siddhpura,
@skywinder Talvez sim em uma versão anterior, mas as versões atuais do Xcode não têm problemas com esta solução.
Darren Ehlers
2

Outra forma de usar variáveis ​​@protected.

@interface SuperClass:NSObject{
  @protected
    SEL protectedMehodSelector;
}

- (void) hackIt;
@end

@implementation SuperClass

-(id)init{

self = [super init];
if(self) {
 protectedMethodSelector = @selector(baseHandling);
 }

return self;
}

- (void) baseHandling {

  // execute your code here
}

-(void) hackIt {

  [self performSelector: protectedMethodSelector];
}

@end

@interface SubClass:SuperClass
@end

@implementation SubClass

-(id)init{

self = [super init];
if(self) {
 protectedMethodSelector = @selector(customHandling);
 }

return self;
}

- (void) customHandling {

  // execute your custom code here
}

@end
marius bardan
fonte
e você pode colocar os IVars protegidos em uma extensão de classe em um arquivo de cabeçalho chamado protected too
malhal
1

Você pode definir o método como um método privado da classe pai e pode usar [super performSelector:@selector(privateMethod)];na classe filha.

Chinthakad
fonte
0

Você pode tipo de fazer isso com uma categoria.

@interface SomeClass (Protected)
-(void)doMadProtectedThings;
@end

@implementation SomeClass (Protected)

- (void)doMadProtectedThings{
    NSLog(@"As long as the .h isn't imported into a class of completely different family, these methods will never be seen. You have to import this header into the subclasses of the super instance though.");
}

@end

Os métodos não são ocultados se você importar a categoria em outra classe, mas você simplesmente não o faz. Devido à natureza dinâmica de Objective-C, é realmente impossível ocultar completamente um método, independentemente do tipo de instância de chamada.

O melhor caminho a seguir é provavelmente a categoria de continuação de classe, conforme respondido por @Brian Westphal, mas você terá que redefinir o método nesta categoria para cada instância de subclasse.

Adam Waite
fonte
0

Uma opção é usar extensão de classe para ocultar métodos.

Em .h:

@interface SomeAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

Em .m:

@interface SomeAppDelegate()
- (void)localMethod;
@end

@implementation SomeAppDelegate

- (void)localMethod
{
}

@end
Oh Ho
fonte
Acho que você nem precisa da @interfacedeclaração no arquivo .m. Você pode simplesmente declarar uma função e usá-la e ela a tratará como privada.
Russ de
1
Observe que isso é verdade apenas com as atualizações recentes no Objetivo C. Antes disso, você tinha que declarar o método em uma interface, caso contrário, você pelo menos receberia um aviso.
Guillaume Laurent
0

Eu geralmente nomeio o método protegido com o prefixo interno:

-(void) internalMethod;
lbsweek
fonte