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


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

Mais comment puis-je les créer?


689
2018-03-09 16:06


origine


Réponses:


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

Par exemple, supposons que vous avez un UIWebView. Si vous souhaitez implémenter son délégué webViewDidStartLoad: méthode, vous pouvez 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'affecter en tant que délégué de la vue Web:

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

Sur le UIWebView côté, il a probablement un code similaire à celui-ci pour voir si le délégué répond à la webViewDidStartLoad: message en utilisant respondsToSelector: et l'envoyer si approprié.

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

La propriété déléguée 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 contient souvent une forte référence à cet objet. (Par exemple, un contrôleur de vue est souvent le délégué d'une vue qu'il contient.)

Faire des délégués pour vos cours

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

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

Ceci est analogue à une interface ou une classe de base abstraite, car elle crée un type spécial pour votre délégué, UIWebViewDelegate dans ce cas. Les implémenteurs délégués devraient adopter ce protocole:

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

Et puis implémentez 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é), vous devez vérifier avec -respondsToSelector: avant d'appeler une méthode particulière.

Appellation

Les méthodes Delegate sont généralement nommées en commençant par le nom de la classe délégante et prennent l'objet délégué en tant que premier paramètre. Ils utilisent aussi souvent une forme testamentaire, devrait ou doit être. Alors, webViewDidStartLoad: (premier paramètre est la vue web) plutôt que loadStarted (ne prenant aucun paramètre) par exemple.

Optimisations de vitesse

Au lieu de vérifier si un délégué répond à un sélecteur chaque fois que nous souhaitons l'envoyer par message, vous pouvez mettre en cache cette information lorsque les délégués sont définis. Une façon très propre de faire cela 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é traite les messages en accédant à notre delegateRespondsTo struct, plutôt qu'en envoyant -respondsToSelector: encore et encore.

Délégués informels

Avant l'existence des protocoles, il était courant d'utiliser un Catégorie sur NSObject déclarer les méthodes qu'un délégué pourrait implémenter. Par exemple, CALayer fait toujours ceci:

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

Cela indique essentiellement au compilateur que tout objet peut être implémenté displayLayer:.

Vous utiliserez alors le même -respondsToSelector: approche comme décrit ci-dessus pour appeler cette méthode. Les délégués mettent simplement en œuvre cette méthode et assignent delegatepropriété, 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 de protocole plus moderne ci-dessus, car cette approche pollue NSObject (ce qui rend l'auto-complétion moins utile) et rend le compilateur difficile à vous prévenir des fautes de frappe et des erreurs similaires.


853
2018-03-09 16:16



La réponse approuvée est bonne, mais si vous cherchez une réponse d'une minute, essayez ceci:

Le fichier MyClass.h devrait ressembler à ceci (ajouter des lignes de délégué 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
}

MyVC.m:

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

Implémenter la méthode du délégué

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

363
2017-09-30 10:25



Lorsque vous utilisez la méthode de protocole formel pour créer un support de délégué, j'ai constaté que vous pouvez vous assurer que la vérification de type correcte (quoique, runtime, pas compiler le temps) 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 votre code d'accesseur délégué (setDelegate). Cela aide à minimiser les erreurs.


18
2018-05-04 20:42



Peut-être que c'est plus dans le sens de ce qui vous manque:

Si vous venez d'un point de vue C ++, les délégués s'habituent un peu - mais en fait, ils fonctionnent simplement.

La façon dont cela fonctionne est que vous définissez un objet que vous avez écrit en tant que délégué à NSWindow, mais votre objet n'a que des implémentations (méthodes) pour une ou plusieurs des nombreuses méthodes de délégué possibles. Donc, quelque chose se passe, et NSWindow veut appeler votre objet - il utilise simplement Objective-c respondsToSelector méthode pour déterminer si votre objet veut appeler cette méthode, puis l'appelle. C'est ainsi que fonctionne l'objectif-c - les méthodes sont recherchées à la demande.

Il est totalement trivial de le faire avec vos propres objets, il n'y a rien de spécial, vous pourriez par exemple avoir un NSArray de 27 objets, tous différents types d'objets, seulement 18 certains d'entre eux ayant la méthode -(void)setToBue; Les 9 autres ne le font pas. Donc appeler setToBlue sur l'ensemble des 18 qui en ont besoin, quelque chose comme ceci:

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

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


17
2018-03-10 00:18



S'il vous plaît! Consultez ci-dessous un tutoriel simple étape par étape pour comprendre comment fonctionne Delegates dans iOS.

Déléguer dans iOS

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

  1. Délégué d'implémentation FirstViewController (qui fournit des données).
  2. SecondViewController déclare le délégué (qui recevra les données).

17
2018-02-27 12:21



Comme une bonne pratique recommandée par Apple, il est bon pour le délégué (qui est un protocole, par définition), de se conformer à NSObject protocole.

@protocol MyDelegate <NSObject>
    ...
@end

& pour créer des méthodes facultatives au sein de votre délégué (c'est-à-dire des méthodes qui ne doivent pas nécessairement être implémentées), vous pouvez utiliser le @optional annotation comme ceci:

@protocol MyDelegate <NSObject>
    ...
    ...
      // Declaration for Methods that 'must' be implemented'
    ...
    ...
    @optional
    ...
      // Declaration for Methods that 'need not necessarily' be implemented by the class conforming to your delegate
    ...
@end

Donc, lorsque vous utilisez des méthodes que vous avez spécifiées comme facultatives, vous devez (dans votre classe) vérifier avec respondsToSelector si la vue (conforme à votre délégué) a effectivement implémenté votre (vos) méthode (s) optionnelle (s) ou non.


15
2017-08-04 08:39



Je pense que toutes ces réponses ont beaucoup de sens une fois que vous comprenez les délégués. Personnellement, je viens de la terre de C / C ++ et avant que les langages procéduraux comme Fortran etc, voici donc mes 2 min de trouver des analogues similaires dans le paradigme C ++.

Si je devais expliquer les délégués à un programmeur C ++ / Java, je dirais

Quels sont les délégués? Ce sont des pointeurs statiques aux classes d'une autre classe. Une fois que vous avez assigné un pointeur, vous pouvez appeler des fonctions / méthodes dans cette classe. Ainsi, certaines fonctions de votre classe sont "déléguées" (In C ++ world - pointeur vers un pointeur d'objet de classe) vers une autre classe.

Quels sont les protocoles? Conceptuellement, il sert à des fins similaires au fichier d'en-tête de la classe que vous attribuez en tant que classe de délégué. Un protocole est un moyen explicite de définir quelles méthodes doivent être implémentées dans la classe dont le pointeur a été défini en tant que délégué dans une classe.

Comment puis-je faire quelque chose de similaire en C ++? Si vous essayiez de le faire en C ++, vous définiriez des pointeurs sur des classes (objets) dans la définition de classe, puis vous les relieriez à d'autres classes qui fourniraient des fonctions supplémentaires en tant que délégués à votre classe de base. Mais ce câblage doit être maîtrisé dans le code et sera maladroit et sujet aux erreurs. Objective C suppose simplement que les programmeurs ne sont pas les meilleurs pour maintenir cette décipline et fournit des restrictions de compilateur pour appliquer une implémentation propre.


10
2018-06-19 10:34



Version rapide

Un délégué est juste une classe qui fait du travail pour une autre classe. Lisez le code suivant pour un exemple de terrain de jeu un peu bête (mais j'espère éclairant) qui montre comment cela se fait dans Swift.

// A protocol is just a list of methods (and/or properties) that must
// be used by any class that adopts the protocol.
protocol OlderSiblingDelegate: class {
    // This protocol only defines one required method
    func getYourNiceOlderSiblingAGlassOfWater() -> String
}

class BossyBigBrother {

    // The delegate is the BossyBigBrother's slave. This position can 
    // be assigned later to whoever is available (and conforms to the 
    // protocol).
    weak var delegate: OlderSiblingDelegate?

    func tellSomebodyToGetMeSomeWater() -> String? {
        // The delegate is optional because there might not be anyone
        // nearby to boss around.
        return delegate?.getYourNiceOlderSiblingAGlassOfWater()
    }
}

// PoorLittleSister conforms to the OlderSiblingDelegate protocol
class PoorLittleSister: OlderSiblingDelegate {

    // This method is repquired by the protocol, but the protocol said
    // nothing about how it needs to be implemented.
    func getYourNiceOlderSiblingAGlassOfWater() -> String {
        return "Go get it yourself!"
    }

}

// initialize the classes
let bigBro = BossyBigBrother()
let lilSis = PoorLittleSister()

// Set the delegate 
// bigBro could boss around anyone who conforms to the 
// OlderSiblingDelegate protocol, but since lilSis is here, 
// she is the unlucky choice.
bigBro.delegate = lilSis

// Because the delegate is set, there is a class to do bigBro's work for him.
// bigBro tells lilSis to get him some water.
if let replyFromLilSis = bigBro.tellSomebodyToGetMeSomeWater() {
    print(replyFromLilSis) // "Go get it yourself!"
}

Dans la pratique, les délégués sont souvent utilisés dans les situations suivantes

  1. Quand une classe doit communiquer des informations à une autre classe
  2. Quand une classe veut autoriser une autre classe à la personnaliser

Les classes n'ont pas besoin de savoir quoi que ce soit à l'avance, sauf que la classe déléguée est conforme au protocole requis.

Je recommande fortement de lire les deux articles suivants. Ils m'ont aidé à mieux comprendre les délégués que les Documentation fait.


9
2017-11-05 17:11



Ok, ce n'est pas vraiment une réponse à la question, mais si vous cherchez comment faire votre propre délégué peut-être quelque chose de beaucoup plus simple pourrait être une meilleure réponse pour vous.

Je n'implémente guère mes délégués parce que j'en ai rarement besoin. Je ne peux avoir qu'un seul délégué pour un objet délégué. Donc, si vous voulez votre délégué pour une communication à sens unique / transmettre des données que vous êtes beaucoup mieux avec des notifications.

NSNotification peut transmettre des objets à plusieurs destinataires et il est très facile à utiliser. Cela fonctionne comme ceci:

Le fichier MyClass.m devrait ressembler à ceci

#import "MyClass.h"
@implementation MyClass 

- (void) myMethodToDoStuff {
//this will post a notification with myClassData (NSArray in this case)  in its userInfo dict and self as an object
[[NSNotificationCenter defaultCenter] postNotificationName:@"myClassUpdatedData"
                                                    object:self
                                                  userInfo:[NSDictionary dictionaryWithObject:selectedLocation[@"myClassData"] forKey:@"myClassData"]];
}
@end

Pour utiliser votre notification dans une autre classe: Ajouter une classe en tant qu'observateur:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherClassUpdatedItsData:) name:@"myClassUpdatedData" object:nil];

Implémentez le sélecteur:

- (void) otherClassUpdatedItsData:(NSNotification *)note {
    NSLog(@"*** Other class updated its data ***");
    MyClass *otherClass = [note object];  //the object itself, you can call back any selector if you want
    NSArray *otherClassData = [note userInfo][@"myClassData"]; //get myClass data object and do whatever you want with it
}

Ne pas oublier de retirer votre classe en tant qu'observateur si

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

8
2017-07-17 04:58



disons que vous avez une classe que vous avez développée et que vous voulez déclarer une propriété de délégué pour pouvoir l'avertir lorsqu'un événement se produit:

@class myClass;

@protocol myClassDelegate <NSObject>

-(void)myClass:(MyClass*)myObject requiredEventHandlerWithParameter:(ParamType*)param;

@optional
-(void)myClass:(MyClass*)myObject optionalEventHandlerWithParameter:(ParamType*)param;

@end


@interface MyClass : NSObject

@property(nonatomic,weak)id< MyClassDelegate> delegate;

@end

donc vous déclarez un protocole dans MyClass fichier d'en-tête (ou un fichier d'en-tête séparé) et déclarez les gestionnaires d'événements obligatoires / facultatifs que votre délégué doit / doit implémenter, puis déclarez une propriété dans MyClass de type (id< MyClassDelegate>) qui signifie toute classe c objective qui est conforme au protocole MyClassDelegate , vous remarquerez que la propriété delegate est déclarée faible, ceci est très important pour empêcher le cycle de conservation (le plus souvent le délégué conserve le MyClass par exemple, si vous avez déclaré que le délégué était retenu, les deux se conserveront l'un l'autre et aucun d'entre eux ne sera jamais libéré).

vous remarquerez aussi que les méthodes de protocole passe le MyClass instance au délégué en tant que paramètre, ceci est la meilleure pratique au cas où le délégué veut appeler certaines méthodes sur MyClass instance et aide également lorsque le délégué se déclare MyClassDelegate à plusieurs MyClass instances, comme lorsque vous avez plusieurs UITableView's instances dans votre ViewController et se déclare en tant que UITableViewDelegate à tous.

et à l'intérieur de votre MyClass vous notifiez le délégué avec les événements déclarés comme suit:

if([_delegate respondsToSelector:@selector(myClass: requiredEventHandlerWithParameter:)])
{
     [_delegate myClass:self requiredEventHandlerWithParameter:(ParamType*)param];
}

vous vérifiez d'abord si votre délégué répond à la méthode de protocole que vous êtes sur le point d'appeler au cas où le délégué ne l'implémente pas et que l'application se bloque alors (même si la méthode de protocole est requise).


8
2018-04-08 13:24