Come eseguire i callback in Objective-C


Risposte:


94

Normalmente, i callback nell'obiettivo C vengono eseguiti con i delegati. Ecco un esempio di implementazione di un delegato personalizzato;


File di intestazione:

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

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

File di implementazione (.m)

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

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

Ciò illustra l'approccio generale. Crea una categoria su NSObject che dichiara i nomi dei tuoi metodi di callback. NSObject non implementa effettivamente questi metodi. Questo tipo di categoria è chiamato protocollo informale, stai solo dicendo che molti oggetti potrebbero implementare questi metodi. Sono un modo per dichiarare in avanti la firma del tipo del selettore.

Successivamente hai un oggetto che è il delegato di "MyClass" e MyClass chiama i metodi delegate sul delegato come appropriato. Se i callback dei delegati sono facoltativi, in genere li proteggerai sul sito di invio con qualcosa come "if ([delegate respondsToSelector: @selector (myClassWillDoSomething :)) {". Nel mio esempio, il delegato deve implementare entrambi i metodi.

Invece di un protocollo informale, puoi anche utilizzare un protocollo formale definito con @protocol. Se lo fai, cambierai il tipo di setter delegato e la variabile di istanza in " id <MyClassDelegate>" invece che solo "id ".

Inoltre, noterai che il delegato non viene mantenuto. Ciò si esegue in genere perché l'oggetto che "possiede" istanze di "MyClass" è in genere anche il delegato. Se MyClass ha mantenuto il suo delegato, ci sarebbe un ciclo di conservazione. È una buona idea nel metodo dealloc di una classe che dispone di un'istanza MyClass ed è il suo delegato per cancellare quel riferimento delegato poiché è un puntatore indietro debole. Altrimenti, se qualcosa mantiene in vita l'istanza MyClass, avrai un puntatore penzolante.


+1 Buona risposta esauriente. La ciliegina sulla torta sarebbe un collegamento a una documentazione Apple più approfondita sui delegati. :-)
Quinn Taylor,

Jon, grazie mille per il tuo aiuto. Apprezzo molto il vostro aiuto. Mi dispiace per questo, ma non sono molto chiaro sulla risposta. Message .m è una classe che si imposta come delegato durante la chiamata alla funzione doSomething. DoSomething è la funzione di callback che l'utente chiama? poiché ho l'impressione che l'utente chiami doSomething e le funzioni di call back siano myClassWillDoSomethingg e myClassDidDoSomething. Inoltre, puoi mostrarmi come creare una classe superiore che richiami la funzione di richiamata. Sono un programmatore C quindi non ho ancora molta familiarità con l'ambiente Obj-C.
ReachConnection

"Messaggio .m" significava solo, nel tuo file .m. Avresti una classe separata, chiamiamola "Foo". Foo avrebbe una variabile "MyClass * myClass", e ad un certo punto Foo direbbe "[myClass setDelegate: self]". In qualsiasi momento successivo, se qualcuno, incluso foo, invocasse doSomethingMethod su quell'istanza di MyClass, foo avrebbe invocato i metodi myClassWillDoSomething e myClassDidDoSomething. In realtà pubblicherò anche un secondo esempio diverso che non utilizza delegati.
Jon Hess

Non credo che .m stia per "messaggio".
Chuck


140

Per completezza, dal momento che StackOverflow RSS ha appena resuscitato casualmente la domanda per me, l'altra opzione (più recente) è quella di utilizzare i blocchi:

@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: cosa significa il carattere "^" in "void (^ _completionHandler) (int someParameter);" significare? Puoi spiegare cosa fa quella linea?
Konrad Höffner

2
Potete fornire una spiegazione del motivo per cui è necessario copiare il gestore di callback?
Elliot Chance

52

Ecco un esempio che tiene fuori i concetti di delegati e fa solo una semplice richiamata.

@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

Tipicamente il metodo - [Foo doSomethingAndNotifyObject: withSelector:] sarebbe asincrono, il che renderebbe il callback più utile di quanto non sia qui.


1
Grazie mille John. Capisco la tua prima implementazione della richiamata dopo i tuoi commenti. Inoltre, la tua seconda implementazione del callback è più semplice. Entrambi sono molto buoni.
ReachConnection

1
Grazie per aver postato questo Jon, è stato molto utile. Ho dovuto cambiare [object performSelectorwithObject: self]; a [object performSelector: selector withObject: self]; per farlo funzionare correttamente.
Banjer

16

Per mantenere questa domanda aggiornata, l'introduzione di ARC in iOS 5.0 significa che ciò può essere ottenuto utilizzando i blocchi in modo ancora più conciso:

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

C'è sempre F **** ng Block Syntax se dimentichi la sintassi di Objective-C.


In + (void)sayHi:(void(^)(NSString *reply))callback;+ (void)sayHi:(void(^)(NSString *))callback;
@interface

Non secondo la suddetta sintassi F **** ng Block : - (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;(Nota parameterTypesno parameters)
Ryan Brodie

4

CallBack: ci sono 4 tipi di callback in Objective C

  1. Tipo di selettore : puoi vedere NSTimer, UIPangesture sono gli esempi di callback del selettore. Utilizzato per un'esecuzione molto limitata del codice.

  2. Tipo delegato : comune e più utilizzato nel framework Apple. UITableViewDelegate, NSNURLConnectionDelegate. Di solito vengono utilizzati per mostrare Download di molte immagini dal server in modo asincrono, ecc.

  3. NSNotifications : NotificationCenter è una delle funzionalità di Objective C che inviava notifiche a molti destinatari nel momento in cui si verificava un evento.
  4. Blocchi : i blocchi sono più comunemente usati nella programmazione Objective C. È un'ottima funzionalità e viene utilizzata per eseguire blocchi di codice. Puoi anche fare riferimento al tutorial per capire: Tutorial sui blocchi

Per favore lasciami se qualsiasi altra risposta per questo. Io lo apprezzerei.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.