Em Objective-C, temos duas maneiras utilizadas para receber e enviar mensagens entre classes: as notificações e delegados. A diferença entre os dois, assim como ao nível da aplicação, depende substancialmente "quanto" - objectos - pode receber uma mensagem. Primeiro, deixe-me mostrar-lhe como é que o conceito de delegado.
Delegar
Imagine que nós temos a nossa classe habitual A (nosso receptor, neste exemplo) que instancia um objeto da classe B (o nosso transmissor). A classe A quer ser notificado quando uma determinada operação, realizada pela classe B, está concluída. Uma implementação em bruto deste conceito, completamente legítimo e funcionando, seria: Classe A passa um ponteiro para a sua classe B, de modo que, quando este quer "avisar" da Classe A, não deve deixar de invocar um método usando o ponteiro para o seu passado.

O código da classe B e, em seguida, seria:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | / / ------------------------------------------------ ---------------------- / / Arquivo ClassB.h @ Classe ClassA; NSObject { @ Interface ClassB: NSObject { ClassA * pointerClassA; } / / Eu fornecer uma propriedade com que o Classe A pode / / Passa o ponteiro assign ) ClassA * pointerClassA; @ Property (atribuir) ClassA * pointerClassA; @ End / / ------------------------------------------------ ---------------------- / / Arquivo ClassB.m # Import "ClassB.h" # Import "ClassA.h" @ Implementação ClassB @ Sintetizar pointerClassA; void ) operazione { - (Void) {operação / / ... operação / / Envia o Classe A notificação de acabamento ; [PointerClassA metodoDiAvviso]; } @ End |
A Classe Classe B sabe que aceita um ponteiro para seu (Classe A e Classe B se conhecem, mas ... você precisa saber neste exemplo) também sabe que quando o B-Class terá completado sua tarefa irá chamar um método chamado metodoDiAvviso . Então, Classe A, tem que fazer isso para implementar tal método e passar um Classe B seu ponteiro. Tudo isso traduzido em código, é:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | / / ------------------------------------------------ ---------------------- / / Arquivo ClassA.h @ Classe ClassB; <NSObject> { @ Interface ClassA: {<NSObject> ClassB * classB; } void ) metodoDiAvviso; - (Void) metodoDiAvviso; void ) metodoQualsiasi; - (Void) metodoQualsiasi; @ End / / ------------------------------------------------ ---------------------- / / Arquivo ClassA.m # Import "ClassA.h" # Import "ClassB.h" @ Implementação ClassA id ) init { - (Id) de inicialização { super init ] ; auto = [super init]; self ) { if (self) { ClassB alloc ] init ] ; classB = [[ClassB alloc] init]; classB.pointerA = self / / Eu me comunico minha ponteiro para o Classe B } retornar auto; } void ) metodoQualsiasi { - (Void) {metodoQualsiasi / / ... ; [ClassB operação]; } void ) metodoDiAvviso { - (Void) {metodoDiAvviso / / Método chamado pela Classe B "%s: la Classe B ha terminato e mi ha avvisato!" , __FUNCTION__ ) ; NSLog (@ "% s: o B-Class acabou e me avisou", __ FUNCTION__); } @ End |
Embora esta técnica funções, mostra todas as suas limitações quando utilizado em contextos também pequenas. Vamos ver quais são as coisas que não são:
- Classes devem conhecer uns aos outros: cada um tem importado as outras definições
- O A-Class deve implementar e declarar o receptor método
- A classe B assume que existe um Class, isto é, o ponteiro
pointerAser explorada - Tudo é feito explicitar os nomes das classes do jogo
- Se você quiser enviar uma mensagem de Classe B para a Classe C deve ser: o alterar o código ou duplicar a turnê
Felizmente, há um muito mais elegante, clara e simples de usar para lidar com essas situações. Nós já falamos sobre em Como criar um delegado com seu próprio protocolo . Objective-C permite definir um protocolo de comunicação através do qual uma ou mais classes podem trocar informações.
Nota: Você pode achar que os protocolos de Objective-C são indicados como uma forma de herdar a interface (e não a execução) de outras classes. Isto é verdade, embora haja algumas diferenças substanciais neste caso, em comparação com o que acontece com outras linguagens.
No nosso caso, o protocolo deve ser definido na classe B, e adotado pela classe A. Na prática, uma classe define qual é o seu protocolo de comunicação. As classes que pretendo receber mensagens do último, adotamos os protocolos.
Vamos analisar o exemplo anterior e depois reescrevê-lo por meio de protocolos e delegados:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | / / ------------------------------------------------ ---------------------- / / Arquivo ClassB.h @ Protocol ClassBDelegate; NSObject { @ Interface ClassB: NSObject { id <ClassBDelegate> delegada; } / / Delegado assign ) id<ClassBDelegate> delegate; @ Property (atribuir) ID <ClassBDelegate> delegada; @ End @ Protocol ClassBDelegate <NSObject> @ Opcional / / Mensagem enviada ao delegado quando a operação void ) metodoDiAvviso; - (Void) metodoDiAvviso; @ End / / ------------------------------------------------ ---------------------- / / Arquivo ClassB.m # Import "ClassB.h" @ Implementação ClassB @ Sintetizar delegado; void ) operazione { - (Void) {operação / / ... operação / / Se houver um delegado e delegado que está em conformidade com o protocolo / / ClassBDelegate enviar avisos de acabamento delegate && [ delegate respondsToSelector : @selector ( metodoDiAvviso ) ] ) { if (&& delegado [delegado respondsToSelector: @ selector (metodoDiAvviso)]) { ; [Delegado metodoDiAvviso]; } } @ End |
No novo código, já podemos notar um significativo passo em frente: não há nenhuma indicação sobre a Classe A. Este último, por sua vez, deve adotar em vez do protocolo explicitamente disponibilizados pela Classe B se ele quer ser um delegado e, em seguida, receber notificações:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | / / ------------------------------------------------ ---------------------- / / Arquivo ClassA.h # Import "ClassB.h" / / eu adotar o protocolo ClassBDelegate < NSObject , ClassBDelegate> { @ Interface ClassA: < NSObject , ClassBDelegate> { ClassB * classB; } void ) metodoQualsiasi; - (Void) metodoQualsiasi; @ End / / ------------------------------------------------ ---------------------- / / Arquivo ClassA.m # Import "ClassA.h" @ Implementação ClassA id ) init { - (Id) de inicialização { super init ] ; auto = [super init]; self ) { if (self) { ClassB alloc ] init ] ; classB = [[ClassB alloc] init]; / / Comunique-se com a classe B para ser o seu vice classB.delegate = auto; } retornar auto; } void ) metodoQualsiasi { - (Void) {metodoQualsiasi / / ... ; [ClassB operação]; } / / De acordo com o protocolo adotado void ) metodoDiAvviso { - (Void) {metodoDiAvviso / / Método chamado pela Classe B "%s: la Classe B ha terminato e mi ha avvisato!" , __FUNCTION__ ) ; NSLog (@ "% s: o B-Class acabou e me avisou", __ FUNCTION__); } @ End |
Este processo é muito mais limpo e mais simples de implementar. As vantagens são:
- A classe que quer delegar (Classe B) não deve saber seus delegados, o suficiente para ele que eles cumpram com o seu protocolo. Desta forma, qualquer classe pode receber mensagens
- A classe que queira receber a mensagem (Classe A) adota explicitamente a / i protocolo / s, que pode ser tomado em parte ou em sua totalidade com base em como você definiu
O único problema com esta abordagem é relacionada com o número de pessoas. No nosso exemplo, a instância da classe B foi criado para toda a classe A. Neste contexto, é óbvio (mas não necessariamente), que é de classe A para ser o delegado. Em uma variante diferente poderia instanciar Classe A Classe C e fornecê-lo como um delegado de Classe B (Classe C vai ter que adotar o ClassBDelegate Protocol). No entanto, o que está claro é que uma instância está ligada apenas um delegado, Classe B não pode ter dois ou mais delegados, não fornecem bens no valor de delegate .
Nomeando
Passar as suas palavras na nomenclatura, isto é, sobre a nomenclatura de protocolos e métodos deste último. O nome do protocolo, se você quiser seguir as regras da Apple, que você normalmente usa para pendurar Delegate no final do nome da classe que define o protocolo. Em outros casos, quando o protocolo não está ligado a qualquer classe particular, a utilização da forma de asa ...ing Inglês, não deve ser confundida com a classe de protocolos. Assim, por exemplo:
1 2 3 4 | Nslook vai NSLooking, nslook parece ser uma classe ClassB definir sua ClasseBDelegate protocolo UIWebView define UIWebViewDelegate ... |
O método definido no protocolo, ao contrário, segue a regra que deve sempre passar o ponteiro de quem enviou a mensagem, seguido pelos parâmetros, se houver. Por exemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | BOOL ) tableView : ( NSTableView * ) tableView shouldSelectRow : ( int ) row; - (BOOL) tableView: ( NSTableView *) tableView shouldSelectRow: (int) linha; BOOL ) application : ( NSApplication * ) sender openFile : ( NSString * ) filename; - (BOOL) Aplicação: ( NSApplication *) remetente openFile: ( NSString *) nome; / / Se não há parâmetros BOOL ) applicationOpenUntitledFile : ( NSApplication * ) sender; - (BOOL) applicationOpenUntitledFile: ( NSApplication *) remetente; / / Usamos "Did" ou "irão" para avisar ao delegado que algo é / / Sucesso ou que algo vai acontecer void ) browserDidScroll : ( NSBrowser * ) sender; - (Void) browserDidScroll: ( NSBrowser *) remetente; NSUndoManager * ) windowWillReturnUndoManager : ( NSWindow * ) window; - ( NSUndoManager *) windowWillReturnUndoManager: ( NSWindow *) janela; / / O prefixo "deve" é usado para pedir ao delegado vez / / Se você quer que algo aconteça BOOL ) windowShouldClose : ( id ) sender; - (BOOL) windowShouldClose: (id) sender; |
Notificações
Quando um objeto ou uma instância de uma classe pode ser compartilhado por vários objetos e você tem a necessidade de alertar todos os objetos afetados, você deve usar um método diferente para comunicar mensagens, os chamados notificações.
Notificações e protocolos / delegados podem facilmente coexistir dentro do mesmo código, o que significa que, se já implementou um protocolo / delegado, não há nada a acrescentar notificações. então deixe-me mostrar-lhe um exemplo usando apenas as suas notificações iniciais.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | / / ------------------------------------------------ ---------------------- / / Arquivo ClassB.h NSObject { @ Interface ClassB: NSObject { } @ End / / ------------------------------------------------ ---------------------- / / Arquivo ClassB.m # Import "ClassB.h" @ Implementação ClassB @ Sintetizar pointerClassA; void ) operazione { - (Void) {operação / / ... operação / / Envia uma notificação a todos aqueles que se inscreveram defaultCenter ] postNotificationName : @ "operazioneTerminata" ] ; [[ NSNotificationCenter defaultCenter] postNotificationName: @ "operazioneTerminata"]; } @ End |
Como pode ser visto a partir do código de cima, esta técnica não acrescentam nada, excepto o envio da notificação. Aqueles que querem ser notificado terá que fazer muito pouco:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | / / ------------------------------------------------ ---------------------- / / Arquivo ClassA.h <NSObject> { @ Interface ClassA: {<NSObject> ClassB * classB; } void ) metodoQualsiasi; - (Void) metodoQualsiasi; @ End / / ------------------------------------------------ ---------------------- / / Arquivo ClassA.m # Import "ClassA.h" @ Implementação ClassA id ) init { - (Id) de inicialização { super init ] ; auto = [super init]; self ) { if (self) { ClassB alloc ] init ] ; classB = [[ClassB alloc] init]; / / Eu registar-se como interessados na mensagem "operazioneTerminata" defaultCenter ] addObserver : self [[ NSNotificationCenter defaultCenter] addObserver: self ( notificaOperazioneTerminata ) selector: @ selector (notificaOperazioneTerminata) "operazioneTerminata" Nome: @ "operazioneTerminata" ] ; objeto: nil]; } retornar auto; } void ) metodoQualsiasi { - (Void) {metodoQualsiasi / / ... ; [ClassB operação]; } / / O nome deste método decide o Classe A e pode ser / / Então qualquer void ) notificaOperazioneTerminata { - (Void) {notificaOperazioneTerminata / / Método chamado pela Classe B "%s: la Classe B ha terminato e mi ha avvisato!" , __FUNCTION__ ) ; NSLog (@ "% s: o B-Class acabou e me avisou", __ FUNCTION__); } void ) dealloc { - (Void) {dealloc / / Eu me retirar o recebimento das notificações relativas à / / Mensagem identificado com a string @ "operazioneTerminata" defaultCenter ] removeObserver : self [[ NSNotificationCenter defaultCenter] removeObserver: self "operazioneTerminata" Nome: @ "operazioneTerminata" ] ; objeto: nil]; ; [Super dealloc]; } @ End |
Nomeando
Aqui, também, algumas palavras sobre a nomeação das notificações, sempre de acordo com as sugestões Apple. O nome da notificação, a qual é um NSString , é normalmente construído de acordo com este critério:
1 | + [ Did | Will ] + [ UniquePartOfName ] + Notification [Nome da classe Associado] + [DID | Will] + [UniquePartOfName] + Notificação |
No exemplo acima, teríamos que colocar no arquivo H Classe B.:
1 2 3 4 5 6 7 8 9 10 | / / No arquivo de cabeçalho da Classe B * const ClassBDidFinishedNotification; extern NSString * const ClassBDidFinishedNotification; / / No arquivo da Classe B implementação const ClassBDidFinishedNotification = @ "ClassBDidFinishedNotification" ; NSString * const ClassBDidFinishedNotification = @ "ClassBDidFinishedNotification"; / / O método na classe A, no entanto, é mais livre, mas poderia ser escrito / / Por exemplo, acrescentando "Notificação" no final - isso, no entanto, tem menos / / Importância void ) finishedNotification; - (Void) finishedNotification; |
Parâmetros
Ambas as técnicas descritas acima permite que você envie mensagens com um número arbitrário de parâmetros. Tomamos o exemplo acima e tentar passar dois parâmetros. No caso do protocolo / delegado, poderíamos ter:
1 2 3 4 5 | @ Protocol ClassBDelegate <NSObject> @ Opcional / / Mensagem enviada ao delegado quando a operação void ) classB : ( ClassB * ) aClassB avvisoConID : ( int ) aID; - (Void) classB: (ClassB *) aClassB avvisoConID: (int) ajuda; @ End |
A implementação deve ter sido escrito:
1 2 3 4 5 6 7 8 | void ) operazione { - (Void) {operação / / ... operação / / Se houver um delegado e delegado que está em conformidade com o protocolo / / ClassBDelegate enviar avisos de acabamento delegate && [ delegate respondsToSelector : @selector ( classB : avvisoConID : ) ] ) { if (&& delegado [delegado respondsToSelector: @ selector (classB: avvisoConID :)]) { self avvisoConID : 57 ] ; [ClassB delegado: self avvisoConID: 57]; } } |
Como você pode ver muito, muito simples.
) contiene un puntatore ad un NSDictionary con la collezione dei parametri. Para as notificações, o discurso é semelhante, embora aqui nós usamos um único parâmetro userInfo que normalmente (se não nil ), contém um ponteiro para um NSDictionary com a coleção de parâmetros. No momento em que a comunicação teria que:
1 2 3 4 5 6 7 8 | void ) operazione { - (Void) {operação / / ... operação / / Envia uma notificação a todos aqueles que se inscreveram defaultCenter ] postNotificationName : @ "operazioneTerminata" [[ NSNotificationCenter defaultCenter] postNotificationName: @ "operazioneTerminata" objeto: self NSDictionary dictionaryWithObject : [ NSNumber numberWithInt : 57 ] userInfo: [ NSDictionary dictionaryWithObject: [ NSNumber numberWithInt: 57] "avvisoConID" ] ] ; forKey: @ "avvisoConID"]]; } |
No momento da leitura de notificação:
1 2 3 4 5 6 7 | void ) notificaOperazioneTerminata : ( NSNotification * ) aNotification { - (Void) notificaOperazioneTerminata: ( NSNotification *) {aNotification / / Método chamado pela Classe B "%s: la Classe B ha terminato e mi ha avvisato!" , __FUNCTION__ ) ; NSLog (@ "% s: o B-Class acabou e me avisou", __ FUNCTION__); avvisoConID = [ [ aNotification userInfo ] objectForKey : @ "avvisoConID" ] ; NSNumber * avvisoConID = [[aNotification userInfo] objectForKey: @ "avvisoConID"]; "%s: %d" , __FUNCTION__, [ avvisoConID intValue ] ) ; NSLog (@ "% s:% d", __ FUNCTION__ [avvisoConID intValue]); } |
Em conclusão, quando usar delegados / protocolos e notificações?
Como vimos ambas as soluções são simples, poderoso e fácil de implementar. Se os eventos na instância de nossa classe pode afetar vários objetos, não temos outra escolha a não ser usar notificações. Em todos os outros casos, os delegados / protocolos ou ambas as soluções.
Normalmente recomendamos a partir da implementação de protocolos e delegados, como se isso deve acontecer de que um evento é de interesse coletivo, sempre podem ser enviados na notificação.










Excelente artigo!
Clara, precisa e cheia de exemplos!
Blog muito interessante!
Saudações
Não é que você poderia ter um último exemplo do uso de delegados? Aqui estamos limitados a definição, mas, em seguida, um exemplo prático de como usá-lo (referindo-se ao código no exemplo)?
[...] A questão da ILeW com um artigo genuíno para explicar melhor, anexando exemplo, como eles funcionam e os delegados [...]
@ ILeW: certamente, veja aqui
muito clara e simples
Devo admitir que a escrita de um pa dificilmente usar delegados criados por mim ... é claro que é uma técnica que eu prometi a usar com mais freqüência, embora forçando a necessidade
parabéns