ios delegates - mobile conf rio 2014
DESCRIPTION
Presented at Mobile Conf Rio 2014, Brazil. The content is in Brazilian Portuguese. Discussion of the Delegate pattern and its implementation in iOS API. Samples from the SDK and a custom interaction using delegates. Shows techniques such as performSelector: calls, delegation through protocol and delegation using blocks. Code samples: https://github.com/osnipso/mobileconf2014 Caelum IP-67 course on iOS (in Brazilian Portuguese): http://www.caelum.com.br/curso-ios-iphone-ipad/TRANSCRIPT
DELEGATESDesvendando esse poderoso padrão
na API do iOS
APRESENTAÇÃO
Osni Oliveira
@osnipso
github.com/osnipso
CONHECIMENTO
• Básico de Objective-C
• Conceitos fundamentais do iOS
• Views
• Actions
•Outlets
slideshare.net/osnipso/primeiros-passos-no-ios-com-objectivec
DELEGATES
• Padrão de Projeto (Design Pattern)
•Divisão de responsabilidades
• Princípio da responsabilidade única
• Comunicação entre objetos
• Inversão de Controle (IoC)
DELEGATES
Colaboração entre objetos
IMPORTÂNCIA
• É *o* padrão mais importante no iOS
•Muito utilizado pela API
•Muito utilizado pelos desenvolvedores
• Imprescindível compreender
•Necessário dominar
• Boa prática utilizar - padronização
DEFINIÇÕES
•Delegation (iOS Developer Library - Cocoa Core Competencies)
https://developer.apple.com/library/ios/documentation/general/conceptual/DevPedia-CocoaCore/Delegation.html
•Delegates and Data Sources (iOS Developer Library - Concepts in Objective-C Programming)
https://developer.apple.com/library/ios/documentation/general/conceptual/CocoaEncyclopedia/DelegatesandDataSources/DelegatesandDataSources.html#//apple_ref/doc/uid/TP40010810-CH11
CONCEITO
Apple (Cocoa Core Competencies):
Delegação é um padrão simples e poderoso em que um objeto em um programa age no lugar de, ou em coordenação com outro objeto.
(...)
O objeto que delega é tipicamente um objeto do framework e o delegado é tipicamente um objeto controller customizado.
CONCEITO
Delega(framework) Delegado
MensagemProtocolo (opcional)
CONCEITO
• Recebe o delegado como dependência (injeção)
• Faz a “primeira parte” da tarefa
• Envia a mensagem
Delega(framework)
CONCEITO
• É injetado para o objeto que delega
• Garante a implementação da mensagem
• Faz a “segunda parte” da tarefa
Delegado
CONCEITO
•Opcional
• Contrato formal
• Acoplamento através do contrato (leve)
Protocolo
CONCEITO
Dois objetos colaboram para realizar uma tarefa.
O primeiro, faz até onde ele consegue fazer.
O que ele não consegue? Delega para o segundo!
CONCEITO
Delega(framework) Delegado
MensagemProtocolo (opcional)
EXEMPLO: UIAlertView
(ViewController.h)
@interface ViewController : UIViewController <UIAlertViewDelegate>
(ViewController.m)
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Exemplo 1" message:@"Começando com delegates" delegate:self cancelButtonTitle:@"Cancela" otherButtonTitles:@"Ok", nil];
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { ... }
EXEMPLO: UIAlertView
UIAlertView ViewController
alertView:clickedButtonAtIndex:
UIAlertViewDelegate
delegatereferência
EXEMPLO: UIAlertView
• Sabe exibir a mensagem e os botões
• Sabe qual botão foi tocado (“clicado”) pelo usuário
•Não sabe o que fazer a partir desse ponto!
•Depende da aplicação
• Aciona o delegado para fazer
EXEMPLO: UIImagePickerController
(ViewController.h)
@interface ViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate>
(ViewController.m)
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;imagePicker.delegate = self; [self presentViewController:imagePicker animated:YES completion:nil];
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { ... }
EXEMPLO: UIImagePickerController
UIImagePickerController ViewController
imagePickerController:didFinishPickingMediaWithInfo:
UINavigationControllerDelegate (*)UIImagePickerControllerDelegate
delegatereferência
EXEMPLO: UIImagePickerController
• Sabe exibir a galeria de imagens/vídeos (câmera, etc.)
• Sabe qual image/vídeo foi selecionado pelo usuário
•Não sabe o que fazer a partir desse ponto!
•Depende da aplicação
• Aciona o delegado para fazer
OUTROS EXEMPLOS
• UITableView
• UITableViewDelegate (interação com UI / exibição avançada)
• UITableViewDataSource (exibição básica)
OUTROS EXEMPLOS
• UIApplicationDelegate
(AppDelegate.h)
@interface AppDelegate : UIResponder <UIApplicationDelegate>
E O CONTRATO?
• Acoplamento a um protocolo específico
•Melhor que acoplar diretamente à classe...
•Obrigatoriedade de implementar todas as mensagens (?)
• Péssimo. Implementações vazias, adapters, etc.
EXEMPLO - SEM PROTOCOLO
• UIPickerController, porém seleciona ao confirmar
• Permite cancelar seleção
• Exibido como modal
EXEMPLO - SEM PROTOCOLO
• MYPickerViewController é quem implementa UIPickerViewDataSource, UIPickerViewDelegate
(MYPickerViewController.h)
@interface MYPickerViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate>
• Exibe conteúdo fixo
• Seguindo a mesma idéia, podemos perguntar a um DataSource o que exibir... (desafio)
EXEMPLO - SEM PROTOCOLO
• Precisamos usar performSelector:
•Delegate pode ser genérico, mas precisa implementar o protocolo NSObject
(MYPickerViewController.h)
@property (weak, atomic) id<NSObject> delegate;
(MYPickerViewController.m)
if ([self.delegate respondsToSelector:@selector(pickedItem:)]) { [self.delegate performSelector:@selector(pickedItem:) withObject:@(self.selectedItem)];}
EXEMPLO - SEM PROTOCOLO
• Problemas com o compilador:
• Até dá pra desligar esse aviso, mas...
EXEMPLO - SEM PROTOCOLO
MYPickerViewController ViewController
cancelPicking (opcional)pickedItem: (opcional)
NSObject
delegatereferência
EXEMPLO - SEM PROTOCOLO
Prós:
•Menos trabalho
• Sem implementações desnecessárias, adapters
Contras:
• Contrato implícito!
• Sem autocomplete
• Sem ajuda do compilador
•Muito limitado!
EXEMPLO - SEM PROTOCOLO
Limitações
• Protocolo NSObject
performSelector:
performSelector:withObject:
performSelector:withObject:withObject:
• Boxing (syntax sugar)
EXEMPLO - COM PROTOCOLO
(MYPickerViewController.h)
@class MYPickerViewController;
@protocol MYPickerViewControllerDelegate <NSObject>
@optional- (void)cancelPickerViewController: (MYPickerViewController *)pickerViewController;
@required- (void)pickerViewController:(MYPickerViewController *)pickerViewController pickedItem:(NSInteger)itemIndex;
@end
@property (weak, atomic) id<MYPickerViewControllerDelegate> delegate;
EXEMPLO - COM PROTOCOLO
(MYPickerViewController.m)
- (IBAction)cancelPicking:(id)sender{ if ([self.delegate respondsToSelector: @selector(cancelPickerViewController:)]) { [self.delegate cancelPickerViewController:self]; }}
- (IBAction)confirmPicking:(id)sender{ [self.delegate pickerViewController:self pickedItem:self.selectedItem];}
EXEMPLO - COM PROTOCOLO
MYPickerViewController ViewController
cancelPickerViewController: (opcional)pickerViewController:pickedItem:
MYPickerViewControllerDelegate
delegatereferência
EXEMPLO - COM PROTOCOLO
Prós:
• Contrato explícito
• Com autocomplete
• Com ajuda do compilador
•Mais flexível e poderoso
Contras:
•Mais trabalho
Com @optional:
• Sem implementações desnecessárias, adapters
• Continua autocomplete!
EXEMPLO - COM BLOCOS
(MYPickerViewController.h)
@property (copy, atomic) void (^cancelBlock) (MYPickerViewController *pickerViewController);
@property (copy, atomic) void (^successBlock) (MYPickerViewController *pickerViewController, NSInteger itemIndex);
EXEMPLO - COM BLOCOS
(MYPickerViewController.m)
- (IBAction)cancelPicking:(id)sender{ if (self.cancelBlock) { self.cancelBlock(self); }}
- (IBAction)confirmPicking:(id)sender{ if (self.successBlock) { self.successBlock(self, self.selectedItem); }}
EXEMPLO - COM BLOCOS
(ViewController.m)
MYPickerViewController *pickerViewController = [[MYPickerViewController alloc] init]; pickerViewController.cancelBlock = ^(MYPickerViewController *pickerViewController) { [self dismissViewControllerAnimated:YES completion:nil]; }; pickerViewController.successBlock = ^(MYPickerViewController *pickerViewController, NSInteger itemIndex) { NSLog(@"Item selecionado: %ld", (unsigned long)itemIndex);
[self dismissViewControllerAnimated:YES completion:nil]; };
[self presentViewController:pickerViewController animated:YES completion:nil];
EXEMPLO - COM BLOCOS
MYPickerViewController ViewController
void (^cancelBlock)(MYPickerViewController *pickerViewController);void (^successBlock)(MYPickerViewController *pickerViewController, NSInteger itemIndex);
Contrato está na assinatura do bloco!
blocobloco (cópia)
EXEMPLO - COM BLOCOS
Prós:
• Contrato explícito
• Com ajuda do compilador
• (Ainda) mais flexível e poderoso
Contras:
• Uma propriedade por callback
• Sintaxe bizarra!
• Problemas de gerenciamento de memória...
EXEMPLO - COM BLOCOS
•Dá pra melhorar um pouquinho a sintaxe...
(MYPickerViewController.h)
typedef void (^MYPickerViewControllerCancelBlock) (MYPickerViewController *pickerViewController);
typedef void (^MYPickerViewControllerSuccessBlock) (MYPickerViewController *pickerViewController, NSInteger itemIndex);
@property (copy, atomic) MYPickerViewControllerCancelBlock cancelBlock;@property (copy, atomic) MYPickerViewControllerSuccessBlock successBlock;
EXEMPLO - COM BLOCOS
• Cuidado com ciclos de retenção!
MYPickerViewController ViewController
blocobloco (cópia de self - “strong”)
picker
pickerViewController (strong)
EXEMPLO - COM BLOCOS
Cuidado:
• Fechamento (closure) no escopo da instância
• Programação funcional - paradigma misto
• Extensão da linguagem C, ainda não padronizada
Curiosidade:
• Sintaxe é a mesma de ponteiro de função (“*” vira “^”)
EXEMPLO - COM BLOCOS
• Resolvendo (possíveis) ciclos de retenção:
(ViewController.m)
typeof(self) __weak weakSelf = self;
pickerViewController.cancelBlock = ^(MYPickerViewController *pickerViewController) { [weakSelf dismissViewControllerAnimated:YES completion:nil]; }; pickerViewController.successBlock = ^(MYPickerViewController *pickerViewController, NSInteger itemIndex) { NSLog(@"Item selecionado: %ld", (unsigned long)itemIndex);
[weakSelf dismissViewControllerAnimated:YES completion:nil]; };
DICAS
• Procure entender o conceito (callback...?)
•Observe as APIs e como elas utilizam o padrão
•Na dúvida, implemente com protocolos (tradicional)
• APIs novas usam cada vez mais blocos - tendência
OBRIGADO!
slideshare.net/osnipso/ios-delegates-mobile-conf-rio-2014
github.com/osnipso/mobileconf2014
@osnipso