776 votes

Comment créer des délégués en Objective-C ?

Je sais comment fonctionnent les délégués et je sais comment je peux les utiliser.

Mais comment les créer ?

919voto

Jesse Rusak Points 33702

Un délégué en Objective-C est un objet qui a été assigné à l'objet delegate propriété d'un autre objet. Pour en créer un, vous devez définir une classe qui implémente les méthodes de délégation qui vous intéressent et marquer cette classe comme implémentant le protocole de délégation.

Par exemple, supposons que vous ayez un UIWebView . Si vous souhaitez implémenter la fonction de son délégué webViewDidStartLoad: vous pourriez créer une classe comme celle-ci :

@interface MyClass<UIWebViewDelegate>
// ...
@end

@implementation MyClass
- (void)webViewDidStartLoad:(UIWebView *)webView { 
    // ... 
}
@end

Vous pouvez ensuite créer une instance de MyClass et l'assigner comme délégué de la vue Web :

MyClass *instanceOfMyClass = [[MyClass alloc] init];
myWebView.delegate = instanceOfMyClass;

Sur le UIWebView il y a probablement un code similaire à celui-ci pour voir si le délégué répond à la demande de l'utilisateur. webViewDidStartLoad: message utilisant respondsToSelector: et l'envoyer si nécessaire.

if([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
    [self.delegate webViewDidStartLoad:self];
}

La propriété du délégué elle-même est généralement déclarée weak (en ARC) ou assign (pré-ARC) pour éviter les boucles de retenue, puisque le délégué d'un objet détient souvent une référence forte à cet objet. (Par exemple, un contrôleur de vue est souvent le délégué d'une vue qu'il contient).

Créer des délégués pour vos classes

Pour définir vos propres délégués, vous devez déclarer leurs méthodes quelque part, comme indiqué dans le chapitre sur les délégués de l'UE. Apple Docs sur les protocoles . Vous déclarez généralement un protocole formel. La déclaration, paraphrasée à partir de UIWebView.h, ressemble à ceci :

@protocol UIWebViewDelegate <NSObject>
@optional
- (void)webViewDidStartLoad:(UIWebView *)webView;
// ... other methods here
@end

Cela est analogue à une interface ou à une classe de base abstraite, car cela crée un type spécial pour votre délégué, UIWebViewDelegate dans ce cas. Les responsables de la mise en œuvre des délégués devront adopter ce protocole :

@interface MyClass <UIWebViewDelegate>
// ...
@end

Et ensuite, implémenter les méthodes dans le protocole. Pour les méthodes déclarées dans le protocole comme @optional (comme la plupart des méthodes de délégués), vous devez vérifier avec -respondsToSelector: avant d'appeler une méthode particulière sur elle.

Nommer

Les méthodes de délégation sont généralement nommées en commençant par le nom de la classe de délégation, et prennent l'objet de délégation comme premier paramètre. Elles utilisent aussi souvent la forme will-, should- ou did-. Ainsi, webViewDidStartLoad: (le premier paramètre est la vue web) plutôt que loadStarted (ne prenant aucun paramètre) par exemple.

Optimisations de la vitesse

Au lieu de vérifier si un délégué répond à un sélecteur chaque fois que nous voulons lui envoyer un message, vous pouvez mettre cette information en cache lorsque les délégués sont définis. Une façon très propre de le faire est d'utiliser un champ de bits, comme suit :

@protocol SomethingDelegate <NSObject>
@optional
- (void)something:(id)something didFinishLoadingItem:(id)item;
- (void)something:(id)something didFailWithError:(NSError *)error;
@end

@interface Something : NSObject
@property (nonatomic, weak) id <SomethingDelegate> delegate;
@end

@implementation Something {
  struct {
    unsigned int didFinishLoadingItem:1;
    unsigned int didFailWithError:1;
  } delegateRespondsTo;
}
@synthesize delegate;

- (void)setDelegate:(id <SomethingDelegate>)aDelegate {
  if (delegate != aDelegate) {
    delegate = aDelegate;

    delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)];
    delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)];
  }
}
@end

Ensuite, dans le corps, nous pouvons vérifier que notre délégué gère les messages en accédant à notre fichier delegateRespondsTo plutôt qu'en envoyant -respondsToSelector: encore et encore.

Délégués informels

Avant l'existence des protocoles, il était courant d'utiliser une catégorie en NSObject pour déclarer les méthodes qu'un délégué pourrait mettre en œuvre. Par exemple, CALayer le fait toujours :

@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end

Cela indique au compilateur que tout objet peut implémenter displayLayer: .

Vous utiliserez alors le même -respondsToSelector: comme décrit ci-dessus pour appeler cette méthode. Les délégués implémentent cette méthode et assignent le delegate et c'est tout (il n'y a pas de déclaration de conformité à un protocole). Cette méthode est courante dans les bibliothèques d'Apple, mais le nouveau code devrait utiliser l'approche protocolaire plus moderne ci-dessus, puisque cette approche pollue NSObject (ce qui rend la complétion automatique moins utile) et empêche le compilateur de vous avertir des fautes de frappe et autres erreurs similaires.

0 votes

Je pense que vous devez lancer le unsigned int type à BOOL comme valeur de retour de delegate respondsToSelector est de type BOOL .

0 votes

Le délégué peut-il être utilisé pour le polymorphisme comme en C++ ?

0 votes

@Dan Oui, bien sûr. Les protocoles en général sont utilisés pour le polymorphisme.

397voto

Tibidabo Points 10510

La réponse approuvée est excellente, mais si vous cherchez une réponse en une minute, essayez ceci :

Le fichier MyClass.h devrait ressembler à ceci (ajoutez les lignes de délégués avec des commentaires !)

#import <BlaClass/BlaClass.h>

@class MyClass;             //define class, so protocol can see MyClass
@protocol MyClassDelegate <NSObject>   //define delegate protocol
    - (void) myClassDelegateMethod: (MyClass *) sender;  //define delegate method to be implemented within another class
@end //end protocol

@interface MyClass : NSObject {
}
@property (nonatomic, weak) id <MyClassDelegate> delegate; //define MyClassDelegate as delegate

@end

Le fichier MyClass.m devrait ressembler à ceci

#import "MyClass.h"
@implementation MyClass 
@synthesize delegate; //synthesise  MyClassDelegate delegate

- (void) myMethodToDoStuff {
    [self.delegate myClassDelegateMethod:self]; //this will call the method implemented in your other class    
}

@end

Pour utiliser votre délégué dans une autre classe (UIViewController appelé MyVC dans ce cas) MyVC.h :

#import "MyClass.h"
@interface MyVC:UIViewController <MyClassDelegate> { //make it a delegate for MyClassDelegate
}

MonVC.m :

myClass.delegate = self;          //set its delegate to self somewhere

Mettre en œuvre la méthode du délégué

- (void) myClassDelegateMethod: (MyClass *) sender {
    NSLog(@"Delegates are great!");
}

4 votes

C'est génial d'utiliser cette réponse comme référence rapide. Mais pourquoi la propriété delegate dans votre MyClass.h est-elle marquée comme 'IBOutlet' ?

4 votes

@ArnovanderMeer Bien vu ! Je ne me souviens plus pourquoi. J'en ai besoin dans mon projet, mais pas dans cet exemple, je l'ai donc supprimé.

0 votes

Merci. Aussi belle et complète que soit la réponse acceptée, j'apprends mieux avec des exemples de code compacts. C'est bien d'avoir deux réponses.

18voto

umop Points 1068

Lorsque l'on utilise la méthode du protocole formel pour créer le support des délégués, j'ai constaté que l'on peut assurer une vérification correcte des types (bien que ce soit au moment de l'exécution et non de la compilation) en ajoutant quelque chose comme :

if (![delegate conformsToProtocol:@protocol(MyDelegate)]) {
    [NSException raise:@"MyDelegate Exception"
                format:@"Parameter does not conform to MyDelegate protocol at line %d", (int)__LINE__];
}

dans le code de votre accesseur de délégué (setDelegate). Cela permet de minimiser les erreurs.

17voto

Tom Andersen Points 3523

Peut-être que cela correspond davantage à ce qui vous manque :

Si vous venez d'un point de vue similaire à celui du C++, il faut un peu de temps pour s'habituer aux délégués, mais en fait, "ils fonctionnent tout simplement".

Le principe est le suivant : vous définissez un objet que vous avez écrit en tant que délégué de NSWindow, mais votre objet ne possède que des implémentations (méthodes) pour une ou quelques unes des nombreuses méthodes de délégué possibles. Il se passe donc quelque chose, et NSWindow veut appeler votre objet - il utilise simplement la fonction respondsToSelector pour déterminer si votre objet veut que cette méthode soit appelée, puis l'appelle. C'est ainsi que fonctionne objective-c - les méthodes sont recherchées à la demande.

Il est tout à fait trivial de faire cela avec vos propres objets, il n'y a rien de spécial qui se passe, vous pourriez par exemple avoir un NSArray de 27 objets, tous de types différents, dont 18 seulement ont la méthode -(void)setToBue; Les 9 autres ne le font pas. Donc pour appeler setToBlue sur tous les 18 qui en ont besoin, quelque chose comme ça :

for (id anObject in myArray)
{
  if ([anObject respondsToSelector:@selector(@"setToBlue")])
     [anObject setToBlue]; 
}

L'autre aspect des délégués est qu'ils ne sont pas conservés, donc vous devez toujours définir le délégué à nil dans votre MyClass dealloc méthode.

16voto

RDC Points 9222

Veuillez consulter le tutoriel simple étape par étape ci-dessous pour comprendre comment les délégués fonctionnent dans iOS.

Délégué dans iOS

J'ai créé deux ViewControllers (pour envoyer des données de l'un à l'autre)

  1. Le délégué du FirstViewController (qui fournit les données).
  2. SecondViewController déclare le délégué (qui recevra les données).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X