¿Cómo realizar funciones de devolución de llamada en Objective-C?
Solo me gustaría ver algunos ejemplos completos y debería entenderlos.
¿Cómo realizar funciones de devolución de llamada en Objective-C?
Solo me gustaría ver algunos ejemplos completos y debería entenderlos.
Respuestas:
Normalmente, las devoluciones de llamada en el objetivo C se realizan con delegados. A continuación, se muestra un ejemplo de una implementación de delegado personalizado;
Archivo de cabecera:
@interface MyClass : NSObject {
id delegate;
}
- (void)setDelegate:(id)delegate;
- (void)doSomething;
@end
@interface NSObject(MyDelegateMethods)
- (void)myClassWillDoSomething:(MyClass *)myClass;
- (void)myClassDidDoSomething:(MyClass *)myClass;
@end
Archivo de implementación (.m)
@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. 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 llama protocolo informal, solo está diciendo que muchos objetos podrían implementar estos métodos. Son una forma de declarar hacia adelante la firma de tipo del selector.
A continuación, tiene algún objeto que sea el delegado de "MyClass" y MyClass llama a los métodos de delegado en el delegado según corresponda. Si las devoluciones de llamada de los delegados son opcionales, normalmente las protegerá en el sitio de envío con algo como "if ([delegate respondsToSelector: @selector (myClassWillDoSomething :)) {". En mi ejemplo, se requiere que el delegado implemente ambos métodos.
En lugar de un protocolo informal, también puede utilizar un protocolo formal definido con @protocol. Si lo hace, cambiaría el tipo de definidor delegado y la variable de instancia para que sea " id <MyClassDelegate>
" en lugar de solo " id
".
Además, notará que el delegado no se retiene. Normalmente, esto se hace porque el objeto que "posee" instancias de "MyClass" suele ser también el delegado. Si MyClass retuvo a 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 borrar esa referencia de delegado, ya que es un puntero hacia atrás débil. De lo contrario, si algo mantiene viva la instancia de MyClass, tendrá un puntero colgando.
Para completar, dado que StackOverflow RSS simplemente resucitó aleatoriamente la pregunta para mí, la otra opción (más nueva) es usar 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);
}];
Aquí hay un ejemplo que mantiene los conceptos de delegados fuera, y solo hace una llamada cruda.
@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
Normalmente, el método - [Foo doSomethingAndNotifyObject: withSelector:] sería asíncrono, lo que haría que la devolución de llamada fuera más útil que aquí.
Para mantener esta pregunta actualizada, la introducción de ARC en iOS 5.0 significa que esto se puede lograr usando Blocks de manera 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 una maldita sintaxis de bloques si alguna vez olvidas la sintaxis de bloques de Objective-C.
+ (void)sayHi:(void(^)(NSString *reply))callback;
no+ (void)sayHi:(void(^)(NSString *))callback;
- (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;
(Nota parameterTypes
no parameters
)
Devolución de llamada: hay 4 tipos de devolución de llamada en Objective C
Tipo de selector : puede ver NSTimer, UIPangesture son los ejemplos de devolución de llamada del selector. Se utiliza para una ejecución de código muy limitada.
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 asincrónica, etc.
Por favor, déjeme si tiene alguna otra respuesta. Te lo agradeceré.