Como executar funções de retorno de chamada no Objective-C?
Gostaria apenas de ver alguns exemplos completos e devo entender.
Como executar funções de retorno de chamada no Objective-C?
Gostaria apenas de ver alguns exemplos completos e devo entender.
Respostas:
Normalmente, retornos de chamada no objetivo C são feitos com delegados. Aqui está um exemplo de uma implementação delegada personalizada;
Arquivo de cabeçalho:
@interface MyClass : NSObject {
id delegate;
}
- (void)setDelegate:(id)delegate;
- (void)doSomething;
@end
@interface NSObject(MyDelegateMethods)
- (void)myClassWillDoSomething:(MyClass *)myClass;
- (void)myClassDidDoSomething:(MyClass *)myClass;
@end
Arquivo de implementação (.m)
@implementation MyClass
- (void)setDelegate:(id)aDelegate {
delegate = aDelegate; /// Not retained
}
- (void)doSomething {
[delegate myClassWillDoSomething:self];
/* DO SOMETHING */
[delegate myClassDidDoSomething:self];
}
@end
Isso ilustra a abordagem geral. Você cria uma categoria no NSObject que declara os nomes dos seus métodos de retorno de chamada. NSObject, na verdade, não implementa esses métodos. Esse tipo de categoria é chamado de protocolo informal, você está apenas dizendo que muitos objetos podem implementar esses métodos. Eles são uma maneira de encaminhar a declaração de tipo do seletor.
Em seguida, você tem algum objeto como o delegado de "MyClass" e MyClass chama os métodos de delegado no delegado, conforme apropriado. Se seus retornos de chamada do delegado forem opcionais, você normalmente os protegerá no site de envio com algo como "if ([delegate respondesToSelector: @selector (myClassWillDoSomething :)) {". No meu exemplo, o delegado é necessário para implementar os dois métodos.
Em vez de um protocolo informal, você também pode usar um protocolo formal definido com @protocol. Se você fizer isso, altere o tipo do configurador de delegado e a variável de instância para " id <MyClassDelegate>
" em vez de apenas " id
".
Além disso, você notará que o delegado não é mantido. Isso geralmente é feito porque o objeto que "possui" instâncias de "MyClass" também é normalmente o delegado. Se o MyClass mantivesse seu delegado, haveria um ciclo de retenção. É uma boa idéia no método desalocado de uma classe que tenha uma instância MyClass e é seu representante para limpar essa referência de delegado, pois é um ponteiro fraco para trás. Caso contrário, se algo estiver mantendo viva a instância MyClass, você terá um ponteiro pendente.
Para ser completo, como o StackOverflow RSS apenas ressuscitou aleatoriamente a pergunta para mim, a outra opção (mais recente) é usar blocos:
@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);
}];
Aqui está um exemplo que mantém os conceitos de delegados afastados e faz apenas uma chamada bruta de volta.
@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, o método - [Foo doSomethingAndNotifyObject: withSelector:] seria assíncrono, o que tornaria o retorno de chamada mais útil do que aqui.
Para manter essa pergunta atualizada, a introdução do ARC no iOS 5.0 significa que isso pode ser alcançado usando Blocos de maneira ainda mais 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);
}];
Sempre existe a sintaxe do bloco, se você esquecer a sintaxe do bloco do Objective-C.
+ (void)sayHi:(void(^)(NSString *reply))callback;
não deve ser #+ (void)sayHi:(void(^)(NSString *))callback;
- (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;
(Nota parameterTypes
não parameters
)
CallBack: Existem 4 tipos de retorno de chamada no Objetivo C
Tipo de seletor : você pode ver NSTimer, UIPangesture e os exemplos de retorno de chamada do seletor. Usado para execução muito limitada de código.
Tipo de Delegado : Comum e mais usado na estrutura da Apple. UITableViewDelegate, NSNURLConnectionDelegate. Eles geralmente são usados para mostrar o download de muitas imagens do servidor de forma assíncrona etc.
Por favor, deixe-me se houver outra resposta para isso. Eu aprecio isso.