So führen Sie Rückrufe in Objective-C durch


119

Wie führe ich Rückruffunktionen in Objective-C aus?

Ich möchte nur einige fertige Beispiele sehen und ich sollte es verstehen.

Antworten:


94

Normalerweise werden Rückrufe in Ziel C mit Delegierten durchgeführt. Hier ist ein Beispiel für eine benutzerdefinierte Delegatenimplementierung.


Header-Datei:

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

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

Implementierungsdatei (.m)

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

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

Das zeigt den allgemeinen Ansatz. Sie erstellen eine Kategorie in NSObject, die die Namen Ihrer Rückrufmethoden deklariert. NSObject implementiert diese Methoden nicht. Diese Art von Kategorie wird als informelles Protokoll bezeichnet. Sie sagen nur, dass viele Objekte diese Methoden implementieren könnten. Sie sind eine Möglichkeit, die Typensignatur des Selektors weiterzuleiten.

Als nächstes haben Sie ein Objekt, das der Delegat von "MyClass" ist, und MyClass ruft die Delegatmethoden für den Delegaten entsprechend auf. Wenn Ihre Delegaten-Rückrufe optional sind, schützen Sie sie normalerweise am Versandort mit "if ([Delegat antwortetToSelector: @selector (myClassWillDoSomething :)) {". In meinem Beispiel muss der Delegat beide Methoden implementieren.

Anstelle eines informellen Protokolls können Sie auch ein mit @protocol definiertes formelles Protokoll verwenden. Wenn Sie dies tun, ändern Sie den Typ des Delegatensetzers und der Instanzvariablen in " id <MyClassDelegate>" anstatt nur " id".

Außerdem werden Sie feststellen, dass der Delegat nicht beibehalten wird. Dies geschieht normalerweise, weil das Objekt, das Instanzen von "MyClass" "besitzt", normalerweise auch der Delegat ist. Wenn MyClass seinen Delegaten behalten würde, würde es einen Aufbewahrungszyklus geben. Bei der Dealloc-Methode einer Klasse mit einer MyClass-Instanz und ihrem Delegaten ist es eine gute Idee, diese Delegatenreferenz zu löschen, da es sich um einen schwachen Rückzeiger handelt. Andernfalls haben Sie einen baumelnden Zeiger, wenn etwas die MyClass-Instanz am Leben hält.


+1 Gute gründliche Antwort. Icing on the Cake wäre ein Link zu einer ausführlicheren Apple-Dokumentation zu Delegierten. :-)
Quinn Taylor

Jon, vielen Dank für deine Hilfe. Ich schätze deine Hilfe sehr. Das tut mir leid, aber die Antwort ist mir nicht klar. Message .m ist eine Klasse, die sich während des Funktionsaufrufs doSomething als Delegat festlegt. Ist doSomething die Rückruffunktion, die der Benutzer aufruft? da ich den Eindruck habe, dass der Benutzer doSomething aufruft und Ihre Rückruffunktionen myClassWillDoSomethingg & myClassDidDoSomething sind. Können Sie mir auch zeigen, wie Sie eine höhere Klasse erstellen, die die Rückruffunktion aufruft? Ich bin ein C-Programmierer, daher bin ich mit der Obj-C-Umgebung noch nicht allzu vertraut.
ReachConnection

"Nachricht .m" bedeutete nur, in Ihrer .m-Datei. Sie hätten eine separate Klasse, nennen wir sie "Foo". Foo hätte eine "MyClass * myClass" -Variable und irgendwann würde Foo "[myClass setDelegate: self]" sagen. Wenn jemand danach, einschließlich foo, die doSomethingMethod auf dieser Instanz von MyClass aufruft, werden die Methoden myClassWillDoSomething und myClassDidDoSomething von foo aufgerufen. Ich werde eigentlich auch nur ein zweites anderes Beispiel posten, das keine Delegaten verwendet.
Jon Hess

Ich glaube nicht, dass die .m für "Nachricht" steht.
Chuck


139

Der Vollständigkeit halber besteht die andere (neuere) Option darin, Blöcke zu verwenden, da StackOverflow RSS die Frage für mich nur zufällig wiederbelebt hat:

@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: Was bedeutet das "^" - Zeichen in "void (^ _completionHandler) (int someParameter);" bedeuten? Können Sie erklären, was diese Zeile tut?
Konrad Höffner

2
Können Sie erklären, warum Sie den Callback-Handler kopieren müssen?
Elliot Chance

52

Hier ist ein Beispiel, das die Konzepte von Delegierten fernhält und nur einen rohen Rückruf ausführt.

@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

Normalerweise ist die Methode - [Foo doSomethingAndNotifyObject: withSelector:] asynchron, was den Rückruf nützlicher macht als hier.


1
Vielen Dank John. Ich verstehe Ihre erste Rückrufimplementierung nach Ihren Kommentaren. Außerdem ist Ihre zweite Rückrufimplementierung einfacher. Beide sind sehr gut.
ReachConnection

1
Danke, dass du diesen Jon gepostet hast, er war sehr hilfreich. Ich musste [object performSelectorwithObject: self] ändern; to [Objekt performSelector: Selektor withObject: self]; damit es richtig funktioniert.
Banjer

16

Um diese Frage auf dem neuesten Stand zu halten, bedeutet die Einführung von ARC in iOS 5.0, dass dies mithilfe von Blöcken noch präziser erreicht werden kann:

@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);
}];

Es gibt immer F **** ng Block Syntax, wenn Sie jemals die Block Syntax von Objective-C vergessen haben.


In @interface sollte + (void)sayHi:(void(^)(NSString *reply))callback;nicht sein+ (void)sayHi:(void(^)(NSString *))callback;
Tim007

Nicht gemäß der oben genannten F **** ng Block Syntax : - (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;(Anmerkung parameterTypesnicht parameters)
Ryan Brodie

4

CallBack: In Ziel C gibt es 4 Arten von Rückrufen

  1. Selektortyp : Sie können NSTimer sehen, UIPangesture sind die Beispiele für Selector-Rückruf. Wird für die sehr eingeschränkte Ausführung von Code verwendet.

  2. Delegatentyp : Häufig und am häufigsten im Apple Framework verwendet. UITableViewDelegate, NSNURLConnectionDelegate. Sie werden normalerweise verwendet, um das asynchrone Herunterladen vieler Bilder vom Server usw. anzuzeigen.

  3. NSNotifications : NotificationCenter ist eine der Funktionen von Objective C, mit der viele Empfänger zum Zeitpunkt des Ereignisses benachrichtigt wurden.
  4. Blöcke : Blöcke werden häufiger in der Objective C-Programmierung verwendet. Es ist eine großartige Funktion und wird zum Ausführen von Codeabschnitten verwendet. Sie können sich auch auf das Tutorial beziehen, um Folgendes zu verstehen: Blockiert das Tutorial

Bitte lassen Sie mich, wenn eine andere Antwort darauf. Ich werde es zu schätzen wissen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.