2009-06-18 11 views
109

¿Cómo se realizan las funciones de devolución de llamada en Objective-C?Cómo realizar una devolución de llamada en Objective-C

me gustaría ver algunos ejemplos terminados y yo debería entenderlo.

+3

Esta pregunta es increíble, y las respuestas son realmente útiles. –

+0

no me imagino que esto es TAN difícil de entender ......... –

Respuesta

89

Normalmente, las devoluciones de llamada en C objetiva se llevan a cabo con los delegados. Aquí hay un ejemplo de una implementación de delegado personalizada;

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

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


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

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

Eso ilustra el enfoque general. Usted crea una categoría en NSObject que declara los nombres de sus métodos de devolución de llamada. NSObject en realidad no implementa estos métodos. Este tipo de categoría se denomina protocolo informal, solo dice que muchos objetos podrían implementar estos métodos. Son una forma de reenviar declarar la firma de tipo del selector.

siguiente que tiene algún objeto sea el delegado de "MyClass" y MiClase llama a los métodos de delegado en el delegado, según corresponda. Si las devoluciones de llamada de los delegados son opcionales, por lo general las protegerás en el sitio de envío con algo como "if ([delegate respondsToSelector: @selector (myClassWillDoSomething :)) {". En mi ejemplo, el delegado debe implementar ambos métodos.

En lugar de un protocolo informal, también se puede utilizar un protocolo formal definido con @protocol. Si haces eso, cambiarías el tipo del setter de delegado y la variable de instancia para que sea "id <MyClassDelegate>" en lugar de solo "id".

También, usted notará el delegado no se conserva. Esto normalmente se hace porque el objeto que "posee" instancias de "MyClass" suele ser también el delegado. Si MyClass retuvo su delegado, entonces habría un ciclo de retención. Es una buena idea en el método dealloc de una clase que tiene una instancia de MyClass y es su delegado para borrar esa referencia de delegado, ya que es un puntero débil. De lo contrario, si algo mantiene viva la instancia de MyClass, tendrá un puntero colgante.

+0

+1 Buena respuesta completa. Icing on the cake sería un enlace a una documentación de Apple más detallada sobre los delegados. :-) –

+0

Jon, muchas gracias por su ayuda. Realmente aprecio tu ayuda. Lo siento, pero no estoy muy claro en la respuesta. Message .m es una clase que se establece como delegado durante la llamada a la función doSomething. ¿DoSomething es la función de devolución de llamada que el usuario llama? ya que tengo la impresión de que el usuario llama a algo, y sus funciones de devolución de llamada son myClassWillDoSomethingg & myClassDidDoSomething. Además, ¿puede mostrarme cómo crear una clase superior que llame a la función de devolución de llamada? Soy un programador de C, por lo que aún no estoy muy familiarizado con el entorno de Obj-C. – ReachConnection

+0

"Mensaje .m" solo significa, en su archivo .m. Tendría una clase separada, llamémosla "Foo". Foo tendría una variable "MyClass * myClass", y en algún momento Foo diría "[myClass setDelegate: self]". En cualquier momento posterior, si alguien, incluido foo, invoca el método doSomething en esa instancia de MyClass, foo tendrá sus métodos myClassWillDoSomething y myClassDidDoSomething invocados. De hecho, también publicaré un segundo ejemplo diferente que no use delegados. –

46

He aquí un ejemplo que mantiene los conceptos de delegados, y sólo lo hace una llamada prima de vuelta.

@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 

Típicamente el método - [Foo doSomethingAndNotifyObject: withSelector:] sería asíncrono que haría que la devolución de llamada más útil que es aquí.

+1

Muchas gracias John. Entiendo su primera implementación de devolución de llamada después de sus comentarios. Además, su segunda implementación de devolución de llamada es más sencilla. Ambos son muy buenos. – ReachConnection

+1

Gracias por publicar este Jon, fue muy útil. Tuve que cambiar [object performSelectorwithObject: self]; a [object performSelector: selector con Object: self]; para que funcione correctamente – Banjer

+0

Efectivamente, es una respuesta clásica de StackOverflow :) Oye, alguien debería ampliar un poco sobre "Normalmente, el método sería asincrónico ..." ¿quizás un ejemplo de eso? Increíble. – Fattie

130

Para completar, desde StackOverflow RSS resucitado simplemente la pregunta aleatoriamente para mí, la otra opción (más reciente) es el uso de bloques:

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

adoran esta solución, similar a hacer la función anónima para devoluciones de llamadas, sencillo, con menos código para escribir. – David

+2

@Ahruman: ¿Qué significa el carácter "^" en "void (^ _completionHandler) (int someParameter);" ¿media? ¿Podrías explicar lo que hace esa línea? –

+0

Blocks también hace que sea más fácil envolver las devoluciones de llamada de estilo C (punteros de función que toman los parámetros 'void *') en bloques. He escrito un ejemplo de cómo hacer esto aquí: [Envolviendo devoluciones de llamada estilo C con bloques] (http://cutecoder.org/programming/wrapping-style-callbacks-blocks/). – adib

5

Para mantener esta pregunta hasta a la fecha, el IOS 5.0 del introducción de ARC significa esto puede lograrse usando Blocks aún más concisa:

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

siempre hay F****ng Block Syntax si alguna vez olvida la sintaxis del bloque de Objective-C.

+0

En @interface debe ser '+ (void) sayHi: (void (^) (NSString * reply)) callback;' not '+ (void) sayHi: (void (^) (NSString *)) callback;' –

+0

No según el [F **** ng Block Syntax] antes mencionado (http://goshdarnblocksyntax.com/): '- (void) someMethodThatTakesABlock: (returnType (^ nullability) (parameterTypes)) blockName;' (Note 'parameterTypes' no 'parámetros') –

3

RetrLla: Hay 4 tipos de devolución de llamada en Objective C

  1. selector de tipo: Se puede ver NSTimer, UIPangesture son los ejemplos de devolución de llamada de selección. Usado para una ejecución de código muy limitada.

  2. tipo de delegado: común y más utilizado en el marco de Apple. UITableViewDelegate, NSNURLConnectionDelegate. Por lo general, se utilizan para mostrar la descarga de muchas imágenes del servidor de forma asíncrona, etc.

  3. NSNotifications: NotificationCenter es una de las funciones del objetivo C que solía notificar muchos recibientes en el momento en que ocurre un evento.
  4. Bloques: Los bloques se utilizan con más frecuencia en la programación del objetivo C. Es una gran característica y se utiliza para ejecutar trozos de código. También puede consultar el tutorial para comprender: Blocks tutorial

Por favor, permítame si alguna otra respuesta para ello. Lo agradeceré.

Cuestiones relacionadas