Métodos protegidos en Objective-C

112

¿Cuál es el equivalente a los métodos protegidos en Objective-C? Quiero definir métodos que solo las clases derivadas pueden llamar / implementar.

LK.
fuente

Respuestas:

47

No puede declarar un método protegido o privado. La naturaleza dinámica de Objective-C hace que sea imposible implementar controles de acceso para los métodos. (Puede hacerlo modificando en gran medida el compilador o el tiempo de ejecución, con una grave penalización de velocidad, pero por razones obvias esto no se hace).

Tomado de Fuente .

Sachin Shanbhag
fuente
Aunque técnicamente no puede, puede emular variables privadas.
Sharen Eayrs
Lee: si declaras un puntero de función en @protected y asignas una función en el método init, ¿funcionaría?
bikram990
156

Puede simular el acceso protegido y privado a los métodos haciendo lo siguiente:

  • Declare sus métodos privados en una extensión de clase (es decir, una categoría sin nombre declarada cerca de la parte superior del archivo .m de la clase)
  • Declare sus métodos protegidos en un encabezado de subclase: Apple usa este patrón con respecto a UIGestureRecognizer (consulte la documentación y la referencia a UIGestureRecognizerSubclass.h)

Estas protecciones no se aplican, como señaló Sachin, en tiempo de ejecución (como en Java, por ejemplo).

Brian Westphal
fuente
2
Acerca de la solución similar a UIGestureRecognizer: El problema es que si algún código importa la subclase, también importará el encabezado de la subclase y, por lo tanto, tendrá acceso a los métodos "protegidos". ¿Hay alguna manera de evitar eso?
yonix
5
Hola, yonix, la importación del encabezado de la subclase se realizaría dentro del archivo .m, no dentro del archivo .h, por lo que la importación de la subclase no importaría estos métodos protegidos.
Brian Westphal
Sugerencia genial Brian, muchas gracias !! Para el póster original, para las propiedades declaradas, solo asegúrese de usar @dynamic en la implementación de la extensión de clase de la subclase (categoría sin nombre), de modo que, en tiempo de ejecución, se use la implementación de la clase principal
user1046037
1
¿Cómo veo UIGestureRecognizerSubclass.h?
Sharen Eayrs
5
Gracias por señalar cómo lo hace Apple internamente. Me puse un ejemplo completo de cómo poner en práctica las cosas de la misma manera que hace Apple enUIGestureRecognizerSubclass.h
Dirty Henry
14

Esto es lo que hice para que los métodos protegidos fueran visibles para mis subclases, sin requerir que implementaran los métodos en sí. Esto significó que no recibí advertencias del compilador en mi subclase acerca de tener una implementación incompleta.

SuperClassProtectedMethods.h (archivo de protocolo):

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

@interface SuperClass (ProtectedMethods) < SuperClassProtectedMethods >
@end

SuperClass.m: (el compilador ahora te obligará a agregar métodos protegidos)

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

Subclase.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
fuente
2
El significado de protegido es que no se puede llamar externamente. Aún puede llamar a cualquier método definido en la clase independientemente de si son visibles externamente o no.
eonil
Sí, entiendo esto. Este método funciona para el cerebro humano, no para el código compilado real. Pero Objective-C no permite eso (no poder llamar externamente). Siempre puedes performSelectorhacerlo.
Michael Kernahan
1
También puedes hacerlo [(id)obj hiddenMethod]. Decir con precisión, el método protegido no es compatible con Objective-C.
eonil
El problema con esto es que las llamadas clases protegidas no pueden anunciar propiedades. Si no necesita propiedades, entonces cualquiera sabe que puede simplemente agregar categorías protegidas.
Sharen Eayrs
@eonil: "También puedes hacer [(id) obj hiddenMethod]". Sí, puede hacer eso, pero recibirá una advertencia del compilador, si ese método no está en ninguna interfaz incluida.
Kaiserludi
9

Acabo de descubrir esto y me funciona. Para mejorar la respuesta de Adam, en su superclase haga una implementación del método protegido en el archivo .m pero no lo declare en el archivo .h. En su subclase, cree una nueva categoría en su archivo .m con la declaración del método protegido de la superclase y puede usar el método protegido de la superclase en su subclase. En última instancia, esto no evitará que el autor de la llamada del método supuestamente protegido sea forzado en tiempo de ejecución.

/////// 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
fuente
2
En este caso, el compilador todavía lanza una advertencia sobre el método no implementadoprotectedMethod
skywinder
Esta es una buena solución, pero en lugar de crear una categoría (Protegido), puede crear una extensión.
Dharmesh Siddhpura
@skywinder Quizás lo hizo en una versión anterior, pero las versiones actuales de Xcode no tienen ningún problema con esta solución.
Darren Ehlers
2

Otra forma de usar variables @protegidas.

@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
fuente
y puede poner los IVars protegidos en una extensión de clase en un archivo de encabezado llamado protegido también
malhal
1

Puede definir el método como un método privado de la clase principal y puede utilizarlo [super performSelector:@selector(privateMethod)];en la clase secundaria.

chinthakad
fuente
0

Puede especie de hacer esto con una categoría.

@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

Los métodos no están ocultos si importa la categoría en otra clase, pero simplemente no lo hace. Debido a la naturaleza dinámica de Objective-C, en realidad es imposible ocultar completamente un método, independientemente del tipo de instancia de llamada.

La mejor manera de hacerlo es probablemente la categoría de continuación de clase según la respuesta de @Brian Westphal, pero tendrá que redefinir el método en esta categoría para cada instancia de subclases.

Adam Waite
fuente
0

Una opción es usar la extensión de clase para ocultar métodos.

En .h:

@interface SomeAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

En .m:

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

@implementation SomeAppDelegate

- (void)localMethod
{
}

@end
Oh ho
fuente
No creo que ni siquiera necesite la @interfacedeclaración en el archivo .m. Puede simplemente declarar una función y usarla y la tratará como privada.
Russ
1
Tenga en cuenta que esto es cierto solo con las actualizaciones recientes en Objective C. Antes de eso, tenía que declarar el método en una interfaz, de lo contrario, al menos recibiría una advertencia.
Guillaume Laurent
0

Por lo general, nombro el método protegido con prefijo interno:

-(void) internalMethod;
libras semanales
fuente