Méthodes protégées en Objective-C

112

Quel est l'équivalent des méthodes protégées en Objective-C? Je veux définir des méthodes que seules les classes dérivées peuvent appeler / implémenter.

LK.
la source

Réponses:

47

Vous ne pouvez ni déclarer une méthode protégée ni privée. La nature dynamique d'Objective-C rend impossible la mise en œuvre de contrôles d'accès pour les méthodes. (Vous pouvez le faire en modifiant fortement le compilateur ou le runtime, avec une pénalité de vitesse sévère, mais pour des raisons évidentes, cela n'est pas fait.)

Tiré de la source .

Sachin Shanbhag
la source
Alors que techniquement vous ne pouvez pas, vous pouvez émuler des variables privées.
Sharen Eayrs
Lee - si vous déclarez un pointeur de fonction dans @protected et attribuez une fonction dans la méthode init, cela fonctionnerait-il?
bikram990
156

Vous pouvez simuler un accès protégé et privé aux méthodes en procédant comme suit:

  • Déclarez vos méthodes privées dans une extension de classe (c'est-à-dire une catégorie sans nom déclarée vers le haut du fichier .m de classe)
  • Déclarez vos méthodes protégées dans un en-tête de sous-classe - Apple utilise ce modèle en ce qui concerne UIGestureRecognizer (voir la documentation et la référence à UIGestureRecognizerSubclass.h)

Ces protections ne sont pas, comme Sachin l'a noté, appliquées à l'exécution (comme elles le sont en Java, par exemple).

Brian Westphal
la source
2
À propos de la solution de type UIGestureRecognizer: Le problème est que si du code importe la sous-classe, il importera également l'en-tête de la sous-classe et aura donc accès aux méthodes "protégées". Y a-t-il un moyen de contourner cela?
yonix
5
Salut yonix, l'importation de l'en-tête de la sous-classe se ferait à l'intérieur du fichier .m pas à l'intérieur du fichier .h, donc l'importation de la sous-classe n'importerait pas ces méthodes protégées.
Brian Westphal
Cool Suggestion Brian, merci beaucoup !! À l'affiche d'origine, pour les propriétés déclarées, assurez-vous simplement d'utiliser @dynamic dans l'implémentation de l'extension de classe de la sous-classe (catégorie sans nom), de sorte qu'au moment de l'exécution, l'implémentation de la classe parente soit utilisée
user1046037
1
Comment voir UIGestureRecognizerSubclass.h?
Sharen Eayrs
5
Merci d'avoir indiqué comment Apple le fait en interne. J'ai posté un exemple complet de la façon de mettre en œuvre des choses de la même manière qu'Apple le fait dansUIGestureRecognizerSubclass.h
Dirty Henry
14

Voici ce que j'ai fait pour que les méthodes protégées soient visibles par mes sous-classes, sans leur demander d'implémenter les méthodes elles-mêmes. Cela signifiait que je n'ai pas reçu d'avertissements du compilateur dans ma sous-classe concernant une implémentation incomplète.

SuperClassProtectedMethods.h (fichier de protocole):

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

@interface SuperClass (ProtectedMethods) < SuperClassProtectedMethods >
@end

SuperClass.m: (le compilateur va maintenant vous forcer à ajouter des méthodes protégées)

#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
la source
2
Le sens de protégé est qu'il ne peut pas être appelé de l'extérieur. Vous pouvez toujours appeler toutes les méthodes définies dans la classe, qu'elles soient visibles de l'extérieur ou non.
eonil
Oui, je comprends cela. Cette méthode fonctionne pour le cerveau humain, pas pour le code compilé. Mais Objective-C ne permet pas cela (ne pas pouvoir appeler de l'extérieur). Vous pouvez toujours performSelectordessus.
Michael Kernahan
1
Vous pouvez également le faire [(id)obj hiddenMethod]. En disant avec précision, la méthode protégée n'est pas prise en charge dans Objective-C.
eonil
Le problème avec ceci est que vos classes dites protégées ne peuvent pas annoncer des propriétés. Si vous n'avez pas besoin de propriétés, tout le monde sait que vous pouvez simplement ajouter des catégories protégées.
Sharen Eayrs
@eonil: "Vous pouvez également faire [(id) obj hiddenMethod]." Oui, vous pouvez le faire, mais vous recevrez un avertissement du compilateur, si cette méthode ne figure dans aucune interface incluse.
Kaiserludi
9

Je viens de découvrir cela et cela fonctionne pour moi.Pour améliorer la réponse d'Adam, dans votre superclasse, faites une implémentation de la méthode protégée dans le fichier .m mais ne la déclarez pas dans le fichier .h. Dans votre sous-classe, créez une nouvelle catégorie dans votre fichier .m avec la déclaration de la méthode protégée de la superclasse et vous pouvez utiliser la méthode protégée de la superclasse dans votre sous-classe. Cela n'empêchera finalement pas l'appelant de la méthode supposée protégée si elle est forcée au moment de l'exécution.

/////// 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
la source
2
Dans ce cas, le compilateur lance toujours un avertissement concernant une méthode non protectedMethod
implémentée
C'est une bonne solution, mais au lieu de créer une catégorie (Protégé), vous pouvez créer une extension.
Dharmesh Siddhpura
@skywinder Peut-être que c'était le cas dans une version antérieure, mais les versions actuelles de Xcode n'ont aucun problème avec cette solution.
Darren Ehlers
2

Une autre façon d'utiliser les variables @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
la source
et vous pouvez mettre les IVars protégés dans une extension de classe dans un fichier d'en-tête nommé protected aussi
malhal
1

Vous pouvez définir la méthode en tant que méthode privée de la classe parent et l'utiliser [super performSelector:@selector(privateMethod)];dans la classe enfant.

chinthakad
la source
0

Vous pouvez en quelque sorte le faire avec une catégorie.

@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

Les méthodes ne sont pas masquées si vous importez la catégorie dans une autre classe, mais vous ne le faites pas. En raison de la nature dynamique d'Objective-C, il est en fait impossible de masquer complètement une méthode quel que soit le type d'instance appelante.

La meilleure façon de procéder est probablement la catégorie de continuation de classe à laquelle a répondu @Brian Westphal, mais vous devrez redéfinir la méthode dans cette catégorie pour chaque instance sous-classée.

Adam Waite
la source
0

Une option consiste à utiliser l' extension de classe pour masquer les méthodes.

Dans .h:

@interface SomeAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

Dans .m:

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

@implementation SomeAppDelegate

- (void)localMethod
{
}

@end
ohho
la source
Je ne pense même pas que vous ayez besoin de la @interfacedéclaration dans le fichier .m. Vous pouvez simplement déclarer une fonction et l'utiliser et elle la traitera comme privée.
Russ
1
Notez que cela n'est vrai qu'avec les mises à jour récentes de l'Objectif C. Avant cela, vous deviez déclarer la méthode dans une interface sinon vous auriez au moins un avertissement.
Guillaume Laurent
0

Je nomme généralement la méthode protégée avec un préfixe interne:

-(void) internalMethod;
lbweek
la source