Metodi protetti in Objective-C


112

Qual è l'equivalente dei metodi protetti in Objective-C? Voglio definire metodi che solo le classi derivate possono chiamare / implementare.

Risposte:


47

Non è possibile dichiarare un metodo protetto o privato. La natura dinamica di Objective-C rende impossibile implementare i controlli di accesso per i metodi. (Puoi farlo modificando pesantemente il compilatore o il runtime, con una grave penalità di velocità, ma per ovvie ragioni questo non viene fatto.)

Tratto dalla fonte .


Mentre tecnicamente non puoi, puoi emulare variabili private.
Sharen Eayrs

Lee - se dichiari un puntatore a funzione in @protected e assegni una funzione nel metodo init, funzionerebbe?
bikram990

156

È possibile simulare l'accesso protetto e privato ai metodi procedendo come segue:

  • Dichiara i tuoi metodi privati ​​in un'estensione di classe (cioè una categoria senza nome dichiarata vicino all'inizio del file .m della classe)
  • Dichiara i tuoi metodi protetti in un'intestazione di sottoclasse: Apple utilizza questo modello rispetto a UIGestureRecognizer (consulta la documentazione e il riferimento a UIGestureRecognizerSubclass.h)

Queste protezioni non sono, come ha notato Sachin, applicate in fase di esecuzione (come lo sono in Java, per esempio).


2
Informazioni sulla soluzione simile a UIGestureRecognizer: Il problema è che se un codice importa la sottoclasse, importerà anche l'intestazione della sottoclasse e avrà quindi accesso ai metodi "protetti". C'è un modo per aggirare questo?
yonix

5
Ciao yonix, l'importazione per l'intestazione della sottoclasse verrebbe eseguita all'interno del file .m non all'interno del file .h, quindi l'importazione della sottoclasse non importerebbe questi metodi protetti.
Brian Westphal

Fantastico suggerimento Brian, grazie mille !! Per il poster originale, per le proprietà dichiarate, assicurati di utilizzare @dynamic nell'implementazione dell'estensione di classe della sottoclasse (categoria senza nome), in modo che in fase di esecuzione venga utilizzata l'implementazione della classe genitore
user1046037

1
Come vedo UIGestureRecognizerSubclass.h?
Sharen Eayrs

5
Grazie per aver sottolineato come Apple lo fa internamente. Ho pubblicato un esempio completo di come implementare le cose nello stesso modo in cui Apple faUIGestureRecognizerSubclass.h
Dirty Henry

14

Ecco cosa ho fatto per rendere visibili i metodi protetti alle mie sottoclassi, senza richiedere loro di implementare i metodi stessi. Ciò significava che non ricevevo avvisi del compilatore nella mia sottoclasse sull'avere un'implementazione incompleta.

SuperClassProtectedMethods.h (file di protocollo):

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

@interface SuperClass (ProtectedMethods) < SuperClassProtectedMethods >
@end

SuperClass.m: (il compilatore ora ti costringerà ad aggiungere metodi protetti)

#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.

2
Il significato di protetto è che non può essere chiamato esternamente. È comunque possibile chiamare qualsiasi metodo definito nella classe indipendentemente dal fatto che sia visibile esternamente o meno.
eonil

Sì, lo capisco. Questo metodo funziona per il cervello umano, non per il codice compilato effettivo. Ma Objective-C non lo consente (non essere in grado di chiamare esternamente). Puoi sempre performSelectorfarlo.
Michael Kernahan

1
Puoi anche farlo [(id)obj hiddenMethod]. Precisamente, il metodo protetto non è supportato in Objective-C.
eonil

Il problema con questo è che le cosiddette classi protette non possono avere proprietà dell'annuncio. Se non hai bisogno di proprietà, chiunque è ben consapevole che puoi semplicemente aggiungere categorie protette.
Sharen Eayrs

@eonil: "Puoi anche fare [(id) obj hiddenMethod]." Sì, puoi farlo, ma riceverai un avviso dal compilatore, se quel metodo non è in nessuna interfaccia inclusa.
Kaiserludi

9

L'ho appena scoperto e per me funziona. Per migliorare la risposta di Adam, nella tua superclasse fai un'implementazione del metodo protetto nel file .m ma non dichiararlo nel file .h. Nella tua sottoclasse crea una nuova categoria nel tuo file .m con la dichiarazione del metodo protetto della superclasse e puoi usare il metodo protetto della superclasse nella tua sottoclasse. Questo alla fine non impedirà il chiamante del metodo presumibilmente protetto se forzato in fase di esecuzione.

/////// 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

2
In questo caso il compilatore lancia ancora un avviso sul metodo non implementatoprotectedMethod
skywinder

Questa è una buona soluzione, ma invece di creare una categoria (Protetta), puoi creare un'estensione.
Dharmesh Siddhpura

@skywinder Forse lo faceva in una versione precedente, ma le versioni attuali di Xcode non hanno problemi con questa soluzione.
Darren Ehlers

2

Un altro modo per utilizzare le variabili @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

e puoi mettere gli IVars protetti in un'estensione di classe in un file di intestazione chiamato anche protected
malhal

1

È possibile definire il metodo come metodo privato della classe genitore e utilizzarlo [super performSelector:@selector(privateMethod)];nella classe figlia.


0

Si può sorta di farlo con una 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

I metodi non vengono nascosti se importi la categoria in un'altra classe, ma semplicemente non lo fai. A causa della natura dinamica di Objective-C è effettivamente impossibile nascondere completamente un metodo indipendentemente dal tipo di istanza chiamante.

Il modo migliore per procedere è probabilmente la categoria di continuazione della classe a cui ha risposto @Brian Westphal, ma dovrai ridefinire il metodo in questa categoria per ogni istanza sottoclasse.


0

Un'opzione è usare l' estensione di classe per nascondere i metodi.

In .h:

@interface SomeAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

In .m:

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

@implementation SomeAppDelegate

- (void)localMethod
{
}

@end

Non penso che tu abbia nemmeno bisogno della @interfacedichiarazione nel file .m. Puoi semplicemente dichiarare una funzione e usarla e tratterà come privata.
Russ

1
Nota che questo è vero solo con i recenti aggiornamenti in Objective C.Prima di ciò, dovevi dichiarare il metodo in un'interfaccia altrimenti avresti ricevuto almeno un avviso.
Guillaume Laurent

0

Di solito nomino il metodo protetto con il prefisso interno:

-(void) internalMethod;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.