Comment effectuer des rappels dans Objective-C


119

Comment effectuer des fonctions de rappel dans Objective-C?

Je voudrais juste voir quelques exemples complets et je devrais le comprendre.

Réponses:


94

Normalement, les rappels dans l'objectif C sont effectués avec des délégués. Voici un exemple d'implémentation de délégué personnalisé;


En tête de fichier:

@interface MyClass : NSObject {
    id delegate;
}
- (void)setDelegate:(id)delegate;
- (void)doSomething;
@end

@interface NSObject(MyDelegateMethods)
- (void)myClassWillDoSomething:(MyClass *)myClass;
- (void)myClassDidDoSomething:(MyClass *)myClass;
@end

Fichier d'implémentation (.m)

@implementation MyClass
- (void)setDelegate:(id)aDelegate {
    delegate = aDelegate; /// Not retained
}

- (void)doSomething {
    [delegate myClassWillDoSomething:self];
    /* DO SOMETHING */
    [delegate myClassDidDoSomething:self];
}
@end

Cela illustre l'approche générale. Vous créez une catégorie sur NSObject qui déclare les noms de vos méthodes de rappel. NSObject n'implémente pas réellement ces méthodes. Ce type de catégorie s'appelle un protocole informel, vous dites simplement que de nombreux objets peuvent implémenter ces méthodes. Ils sont un moyen de transmettre la déclaration de type du sélecteur.

Ensuite, vous avez un objet être le délégué de «MyClass» et MyClass appelle les méthodes de délégué sur le délégué comme il convient. Si vos rappels de délégué sont facultatifs, vous les garderez généralement sur le site de répartition avec quelque chose comme "if ([delegate respondsToSelector: @selector (myClassWillDoSomething :)) {". Dans mon exemple, le délégué doit implémenter les deux méthodes.

Au lieu d'un protocole informel, vous pouvez également utiliser un protocole formel défini avec @protocol. Si vous faites cela, vous changeriez le type du setter de délégué et la variable d'instance en " id <MyClassDelegate>" au lieu de simplement " id".

De plus, vous remarquerez que le délégué n'est pas conservé. Cela est généralement effectué car l'objet qui "possède" des instances de "MyClass" est généralement également le délégué. Si MyClass retenait son délégué, alors il y aurait un cycle de rétention. C'est une bonne idée dans la méthode dealloc d'une classe qui a une instance MyClass et est son délégué pour effacer cette référence de délégué car il s'agit d'un pointeur arrière faible. Sinon, si quelque chose maintient l'instance MyClass en vie, vous aurez un pointeur suspendu.


+1 Bonne réponse approfondie. Cerise sur le gâteau, ce serait un lien vers une documentation Apple plus approfondie sur les délégués. :-)
Quinn Taylor

Jon, merci beaucoup pour votre aide. J'apprécie vraiment votre aide. Je suis désolé, mais je ne suis pas trop clair sur la réponse. Message .m est une classe qui se définit comme délégué lors de l'appel de la fonction doSomething. Est-ce que doSomething est la fonction de rappel que l'utilisateur appelle? puisque j'ai l'impression que l'utilisateur appelle doSomething et que vos fonctions de rappel sont myClassWillDoSomethingg & myClassDidDoSomething. De plus, pouvez-vous me montrer comment créer une classe supérieure qui appelle la fonction de rappel. Je suis un programmeur C donc je ne suis pas encore trop familier avec l'environnement Obj-C.
ReachConnection

"Message .m" signifie simplement, dans votre fichier .m. Vous auriez une classe séparée, appelons-la "Foo". Foo aurait une variable "MyClass * myClass", et à un moment donné, Foo dirait "[myClass setDelegate: self]". À tout moment après cela, si quelqu'un, y compris foo, invoquait la méthode doSomethingMethod sur cette instance de MyClass, foo verrait ses méthodes myClassWillDoSomething et myClassDidDoSomething invoquées. En fait, je posterai également un deuxième exemple différent qui n'utilise pas de délégués.
Jon Hess

Je ne pense pas que le .m signifie "message".
Chuck


140

Par souci d'exhaustivité, puisque StackOverflow RSS vient de ressusciter la question de manière aléatoire pour moi, l'autre option (plus récente) consiste à utiliser des blocs:

@interface MyClass: NSObject
{
    void (^_completionHandler)(int someParameter);
}

- (void) doSomethingWithCompletionHandler:(void(^)(int))handler;
@end


@implementation MyClass

- (void) doSomethingWithCompletionHandler:(void(^)(int))handler
{
    // NOTE: copying is very important if you'll call the callback asynchronously,
    // even with garbage collection!
    _completionHandler = [handler copy];

    // Do stuff, possibly asynchronously...
    int result = 5 + 3;

    // Call completion handler.
    _completionHandler(result);

    // Clean up.
    [_completionHandler release];
    _completionHandler = nil;
}

@end

...

MyClass *foo = [[MyClass alloc] init];
int x = 2;
[foo doSomethingWithCompletionHandler:^(int result){
    // Prints 10
    NSLog(@"%i", x + result);
}];

2
@Ahruman: Que fait le caractère "^" - dans "void (^ _completionHandler) (int someParameter);" signifier? Pouvez-vous expliquer ce que fait cette ligne?
Konrad Höffner

2
Pouvez-vous expliquer pourquoi vous devez copier le gestionnaire de rappel?
Elliot Chance

52

Voici un exemple qui évite les concepts de délégués et ne fait qu'un rappel brut.

@interface Foo : NSObject {
}
- (void)doSomethingAndNotifyObject:(id)object withSelector:(SEL)selector;
@end

@interface Bar : NSObject {
}
@end

@implementation Foo
- (void)doSomethingAndNotifyObject:(id)object withSelector:(SEL)selector {
    /* do lots of stuff */
    [object performSelector:selector withObject:self];
}
@end

@implementation Bar
- (void)aMethod {
    Foo *foo = [[[Foo alloc] init] autorelease];
    [foo doSomethingAndNotifyObject:self withSelector:@selector(fooIsDone:)];
}

- (void)fooIsDone:(id)sender {
    NSLog(@"Foo Is Done!");
}
@end

Typiquement, la méthode - [Foo doSomethingAndNotifyObject: withSelector:] serait asynchrone, ce qui rendrait le rappel plus utile qu'il ne l'est ici.


1
Merci beaucoup John. Je comprends votre première implémentation de rappel après vos commentaires. De plus, votre deuxième implémentation de rappel est plus simple. Les deux sont très bons.
ReachConnection le

1
Merci d'avoir publié ce Jon, c'était très utile. J'ai dû changer [object performSelectorwithObject: self]; à [objet performSelector: selector withObject: self]; afin de le faire fonctionner correctement.
Banjer

16

Pour garder cette question à jour, l'introduction d' ARC par iOS 5.0 signifie que cela peut être réalisé en utilisant des blocs de manière encore plus concise:

@interface Robot: NSObject
+ (void)sayHi:(void(^)(NSString *))callback;
@end

@implementation Robot
+ (void)sayHi:(void(^)(NSString *))callback {
    // Return a message to the callback
    callback(@"Hello to you too!");
}
@end

[Robot sayHi:^(NSString *reply){
  NSLog(@"%@", reply);
}];

Il y a toujours F **** ng Block Syntax si jamais vous oubliez la syntaxe Block d'Objective-C.


Dans @interface ne devrait + (void)sayHi:(void(^)(NSString *reply))callback;pas être+ (void)sayHi:(void(^)(NSString *))callback;
Tim007

Pas selon la syntaxe du bloc F **** ng susmentionnée : - (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;(Remarque parameterTypesnon parameters)
Ryan Brodie

4

CallBack: Il existe 4 types de callback dans Objective C

  1. Type de sélecteur : Vous pouvez voir NSTimer, UIPangesture sont les exemples de rappel de sélecteur. Utilisé pour une exécution très limitée du code.

  2. Type de délégué : commun et le plus utilisé dans le cadre Apple. UITableViewDelegate, NSNURLConnectionDelegate. Ils sont généralement utilisés pour afficher le téléchargement de nombreuses images à partir du serveur de manière asynchrone, etc.

  3. NSNotifications : NotificationCenter est l'une des fonctionnalités de l'Objectif C qui avait l'habitude de notifier de nombreux récipiendaires au moment où l'événement se produit.
  4. Blocs : les blocs sont plus couramment utilisés dans la programmation Objective C. C'est une fonctionnalité intéressante et utilisée pour exécuter un morceau de code. Vous pouvez également consulter le didacticiel pour comprendre: Didacticiel sur les blocs

S'il vous plaît laissez-moi si une autre réponse pour cela. Ça me ferait plaisir.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.