segunda-feira, 8 de outubro de 2007

Recuperando um ponteiro para class Document

Muitas vezes quando utilizamos o padrão Model-View-Controller (que espero falar aqui no futuro) , precisamos acessar dados contidos na nossa classe Document.

Para fazer isto precisamos de um ponteiro que aponte para o objeto desta classe. Podemos recuperar esta referência através de um método da classe CEikAppUi chamado Document(). Este método retorna um ponteiro genérico do tipo CEikDocument. Para poder utilizar os métodos da nossa classe document precisaremos da um cast da classe base CEikDocument para a nossa classe CMeuDocumento (por exemplo).

Para ilustrar melhor a relação entre as classes aqui está um diagrama de classes. Para a nossa classe Document temos:

Não está explicíto, mas por estas classes começarem com a letra C, sabemos que são derivadas de CBase.

Recuperando a referência da nossa classe Document dentro de AppUi

Classes AppUi são em geral derivadas de CAknAppUi (caso não implemente views) ou CAknViewAppUi (caso a aplicação trabalhe com views). Na relação de hierarquia elas herdam o método Document() de CEikAppUi. Considerando que você precise do ponteiro para Document dentro de sua classe AppUi, basta apenas chamar o método Document(), dando o devido cast.



Exemplo:

// Cast para da classe base para a classe filho.
CMeuDocumento* documento = static_cast(Document());
// Chamando um método qualquer.
documento->MinhaFuncao();
Recuperando a referência para a nossa classe document dentro do container

Classes derivadas de CCoeControl possuem um atributo membro iCoeEnv, que é um ponteiro do tipo CCoeEnv. Esta classe possui um método AppUi() que retorna um ponteiro do tipo CCoeAppUi, iremos utilizar este ponteiro para chamar o método Document() de CEikAppUi. Como vemos no diagrama de classes ao lado, CCoeAppUi não possui o método Document() implementado (ele está implementado em CEikAppUi , filha de CCoeAppUi), tereremos então quer dar um cast para a classe AppUi de nossa aplicação para poder acessar este método.

Exemplo:
// Conversão do tipo CCoeAppUi para CMeuAppUi.
CMeuAppUi* appUi = static_cast(iCoeEnv->AppUi());
// Conversão do tipo CEikDocument para CMeuDocumento.
CMeuDocumento* documento = static_cast(appUi->Document());
// Chamando um método qualquer.
documento->MinhaFuncao();
Referências

Developing Series 60 Applications: A Guide for Symbian OS C++ Developers (cap. 4)

quarta-feira, 3 de outubro de 2007

Utilizando classes Singleton no SymbianOS

Sigleton é um padrão de projeto definido no Livro Design Patterns: Elements of Reusable Object-Oriented Software. Este padrão diz que para uma determinada classe apenas uma única instância deve existir por todo o programa.

Em C++ padrão a forma mais simples de se criar uma classe Singleton é utilizar um ponteiro estático e um método estático. Em SymbianOS isto funciona para EXE mas não para DLL*. Pois este ponteiro estático é considerado como um
Writable Static Data (variável global que existe durante toda a vida do processo), e isto não é permitido para DLLs.

Para fazer a criação de singletons temos duas possibilidades, utilizar a classe base CCoeStatic , ou utilizar Thread Local Storage (TLS). Até o momento nunca utilizei TLS então irei focar na utilização da classe CCoeStatic. A principal desvantagem na utilização de CCoeStatic é que você não pode utiliza-lo em aplicações baseadas em console (sem User Interface).

A lógica utilizada será bem parecida com o singleton em C++ padrão, a diferença é que não iremos utilizar um ponteiro estático. Iremos precisar criar um UID único para a classe (único dentro da aplicação), ele será utilizado para identificar de qual classe iremos obter a instância, caso esteja trabalhando com várias classes Singleton.

Arquivo: ExemploSingleton.h

/*
* Exemplo de criação de classes Singleton utilizando CCoeStatic.
* Autor: Leonardo Soares e Silva
*/

#include // Cabeçalho de CCoeStatic.

class CExemploSingleton : public CCoeStatic
{
public:
/* Retorna uma instância desta classe.
* Na primeira vez que este método for chamado ele fará a criação da instância.
* Em chamadas subsequentes será retornada esta instância.
*/
static CExemploSingleton* InstanceL();
~CExemploSingleton();
private:
/* Construtor precisa ser privado para evitar que outras classes a instancie diretamente.*/
CExemploSingleton();
/* Construtor de segunda-fase caso precise realizar alguma operação que possa abandonar */
void ConstructL();
};

Arquivo: ExemploSingleton.cpp

#include "ExemploSingleton.h"

/* UID definido para a classe CExemploSingleton.
* Este UID precisa ser único para esta classe dentro da aplicação.
*/
const TUid KUidClasseSingleton = { 1 };

/* Construção da classe.
* Passamos para CCoeStatic a UID da classe e prioridade de destruição da instância.
*/
CExemploSingleton::CExemploSingleton() : CCoeStatic( KUidClasseSingleton, EDefaultDestructionPriority )
{

}

CExemploSingleton::~CExemploSingleton()
{
// Não implementado.
}

CExemploSingleton* CExemploSingleton::InstanceL()
{
CExemploSingleton* instancia = static_cast
( CCoeEnv::Static( KUidClasseSingleton ) );
/* Somente entrará neste if na primeira chamada a InstanceL();
* Chamadas subsequentes a este método retornarâo esta instância.
*/
if( !instancia )
{
instancia = new (ELeave) CExemploSingleton();
/* Adiciona o ponteiro ao CleanupStack para garatir que está referência não será perdida caso ConstructL abandone.*/
CleanupStack::PushL( instancia );
/* Chamada ao construtor de segunda-fase */
instacia->ConstructL();
/* Após chamar métodos que possam abandonar , liberar da pilha */
CleanupStack::Pop( instancia );
}

return instancia;
}

void CExemploSingleton::ConstructL();
{
// Não implementado
}

O código por si só é bastante fácil de compreender. Como disse anteriormente basta fazer chamadas ao método InstanceL() para receber a referência da classe Singleton. Na primeira chamada será alocado o ponteiro e nas outras será apenas retornado a instância. Algo que vale ressaltar é a construção da classe:

CExemploSingleton::CExemploSingleton() : CCoeStatic( KUidClasseSingleton, EDefaultDestructionPriority )
{

}

Passamos através do contrutor para CCoeStatic dois argumentos. Uma das definições do construtor de CCoeStatic é:

CCoeStatic(TUid aUid, TInt aDestructionPriority, TScope aScope=EThread);

Onde:

aUid = Uid da classe singleton.
aDestructionPriority = Define com que prioridade a instância será destruída. Quanto maior este valor , mais cedo o objeto será destruído. Valores positivos fazem com que o objeto seja destruído antes de CCoeAppUi enquanto valores negativos fazem com que o objeto seja destruído após CCoeAppUi.
aScope = Define para quem o objeto estará acessível. Por definição é para o Thread da aplicação.

---
* = A partir da versão 8.1 passou a ser suportado.