Objota O mundo orientado a objetos

Delegate Objective-C de uma forma simples e direta

Posted on janeiro 10, 2011

Bom, o que quero apresentar para vocês nesse POST é como utilizar delegate de uma forma direta. Haa mas como seria isso? Simples, vou apresentar um exemplo prático. Então mão na massa!

Bom primeiramente vou colocar aqui para vocês um problema simples, imagine as Classes A, B, C e D.
Nesse contexto as classes B, C e D possuem uma instancia da classe A, e dentro da classe A possui um "evento" de Print, que irá imprimir na tela uma String, porém antes de ser impresso esse texto eu tenho que avisar as classes que implementarão A que eu irei realizar essa ação, mas eu não conheço as classes que estão esperando receber esse "evento", então como resolver esse problema?

Então é ai que entra os delegates, que para quem já programa em JAVA vai entender que não passa de um tipo de Listener usado de uma forma diferente.

A criação de um delegate é realizado através da declaração @protocol no arquivo .h onde também é declarada a Classe.

Então vamos ao exemplo. Primeiramente crie um projeto View-based Application no seu Xcode com o nome de TesteDelegate.

Após criar o projeto vamos criar as classes A, B e C dentro da pasta Classes, clicando com o botão direito encima dela e escolhendo a opção Add -> New File. Feito isso vamos criar um método na classe A ( arquivo A.h ) chamado printString.

E dentro desse arquivo, vamos criar também o protocolo da nossa classe da forma apresentada abaixo, depois irei explicar o que faz cada comando.

//A.h
#import <Foundation/Foundation.h>

@protocol ADelegate <NSObject>

@required
- (void)didPrint;

@optional
- (void)willPrint;

@end

@interface A : NSObject {

	id <ADelegate>delegate;

}

@property(nonatomic, retain) id <ADelegate>delegate;

- (void)printString;

@end

//A.m
#import "A.h"

@implementation A

@synthesize delegate;

- (void)printString {

	if ([delegate respondsToSelector:@selector(willPrint)]) {
		[delegate willPrint];
	}

	NSLog(@"Escreveu a String");

	[delegate didPrint];

}

- (id) init {

	self = [super init];

	if (self != nil) {
		delegate = nil;
	}

	return self;

}

@end

Na classe A nós criamos um protocolo chamado ADelegate, esse protocolo é o delegate em si, e como vocês podem ver na declaração nós tipamos o Protocolo como NSObject para dessa forma usar os métodos da classe NSObject.

Embaixo vocês irão encontrar os métodos didPrint com o indicador @required (que serve apenas para informar que o método é de obrigatória implementação pela classe que será responsável pelo delegate) e willPrint com o indicador @optional (serve para indicar que o método não precisar ser implementado, é opcional).

Agora na @interface da classe A declaramos a variavel delegate do tipo id (tipo de dado genérico no Objective-c). Essa variável irá receber a classe que será responsável pelo delegate. Logo embaixo declaramos a propriedade (prometo que irei fazer um post sobre propriedades no objective-c) para o delegate e dessa forma conseguir enxergar essa variável a partir de outras classes.

Feito isso implementamos o método printString, Nesse método temos algumas coisas importantes para falar.

if ([delegate respondsToSelector:@selector(willPrint)]) {
     [delegate willPrint];
}

Nesse if eu verifico se o objeto delegate (que eu não sei de onde vem e quem é, só sei que deriva do NSObject, por esse motivo eu tipei esse protocolo como NSObject) responde ao método willPrint, essa verificação básicamente "olha" se a classe possui esse método implementado, se sim, eu acho o método, se não ele passa direto.

Essa é a forma que posso tornar meu método de implementação opcional.

Já na linha:

[delegate didPrint];

Caso não esteja implementado o método na classe que está setada como delegate irá gerar um erro na execução do programa.

Dessa forma eu torno o método de implementação obrigatória.

Classe B:

Na classe B é criado uma instância da classe A e setado o delegate da classe A como self (self é o mesmo que this usado nas linguagens como java, php, javascript, etc) para que a classe B seja responsável por receber os eventos (ou mensagens) enviados da classe A.

O mesmo é criado na classe C.

//B.h
#import <Foundation/Foundation.h>
#import "A.h"

@interface B : NSObject <ADelegate> {

}

@end

//B.m
#import "B.h"

@implementation B

- (id) init {

	self = [super init];

	if (self != nil) {

		A *a = [[A alloc] init];
		a.delegate = self;
		[a printString];

	}

	return self;

}

- (void)willPrint {
	NSLog(@"vai printar na classe B");
}

- (void)didPrint {
	NSLog(@"terminou de printar na classe B");
}

@end
//C.h
#import <Foundation/Foundation.h>
#import "A.h"

@interface C : NSObject <ADelegate> {

}

@end

//C.m
#import "C.h"

@implementation C

- (id) init {

	self = [super init];

	if (self != nil) {

		A *a = [[A alloc] init];
		a.delegate = self;
		[a printString];

	}

	return self;

}

- (void)willPrint {
	NSLog(@"vai printar na classe C");
}

- (void)didPrint {
	NSLog(@"terminou de printar na classe C");
}

@end

Bom com as classes já criadas, vamos ao arquivo que irá iniciar o nosso teste. Procure o arquivo main.m que está dentro da pasta Other Sources e então deixe como segue abaixo.


#import <UIKit/UIKit.h>
#import "B.h"
#import "C.h"

int main(int argc, char *argv[]) {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

	[[B alloc] init];
	NSLog(@"--------------------------");
	[[C alloc] init];

    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}

Para executar esse programa e ver o resultado pressione command + enter, ou vá ao menu Build -> Build and Debug. Então após o programa rodar no simulador vá em Run -> console e veja a saida do NSLog que colocamos no código.

E se voce quiser pode tirar o método willPrint e verificar o que vai ocorrer.

E ainda pode ver que vai gerar um erro caso retire o método didPrint.

E para fixar ainda mais a idéia de como funciona o delegate, retire o comando a.delegate da classe B e veja o resultado.

Bom galera, espero que tenham entendido, sei que algumas coisas ficaram faltando explicar, porém vou escrever sobre @property em outro artigo sem dúvidas, porém iria ficar confuso escrever tudo nesse.

Espero que gostem, dúvidas e sugestões postem por favor.

Posted by Pedro

Comentários (4) Trackbacks (0)
  1. Tenho o prazer em dizer que compartilho meus dias de trabalho com o Pedro na mesma equipe aonde trabalhamos, post excelente e direto na explicação …. aprendi e aprendo a cada dia com esse carinha.

    Parabens ao Pedro e todos que fazem parte desta bela iniciativa, de nos trazer novas informações a cada dia.

    abraços,

    Fabio Nocera

  2. Olá Pedro,

    Na linha 20 na declaração da classe A,
    @property(nonatomic, retain) id delegate
    O correto não seria definir o atributo do “setter” do delegate utilizando assign? Uma vez que tu define como retain você incrementa o retain cycle e isso não é necessário no caso de delegates.

    Abraço

  3. Então amigo, o setter não seria necessário, pois por padrão ao você criar a property ele já define o get e o set.

    No caso do assign, pode se usar, porém, imagine se sua classe X que é o delegate for removida da memória e a classe A ainda está alocada. Usando o assign pode gerar um apontamento para um endereço que não está alocado mais na memória, gerando possíveis crash, principalmente quando estamos trabalhando com Theads.

    Obrigado pelo comentário!

  4. Caro Pedro.
    Obrigado por compartilhar estes conhecimentos.
    Sou novato em programacao e objective-c, encontrei neste post uma grande ajuda.
    abs
    Marcelo Felix


Leave a comment

Sem trackbacks