iOS para desarrolladores .NET: categorías y extensiones

jueves, 19 de febrero de 2015


Una vez que ya hemos visto como se definen las clases en Objetive-C en anteriores artículos, en este artículo vamos a ver de que forma podemos añadir o extender clases existentes y vamos a compararlo con C# donde también se pueden extender clases.

Categorias

Una categoría permite extender una clases existente, puede ser una clase nuestra y tendremos el código fuente o puede ser una clase de la que no tenemos el código fuente.

Por ejemplo a la clase de Cocoa NSString podemos añadirle el típico método isNullOrWhiteSpace que tiene string en C#, mediante una categoría podríamos añadirle este método. También es posible conseguirlo mediante la herencia, creándonos una clase que herede de NSString, pero si queremos que el método que añadimos también lo tengan disponible las clases derivadas como NSMutableString, utilizar una categoría es el camino adecuado.

C#

En C# el concepto más parecido a Categoría son las extensiones. Por ejemplo Linq hace uso de extensiones para añadir funcionalidades de filtro a las colecciones. Vamos a ver un ejemplo de una clase Person y le añadimos una extension de una función fullName.
public static class PersonExtension {
  public static string GetFullName(this Person p) {
    return string.Format("%1 %2", p.FirstName, p.LastName);
  }
}


Añadiendo el using del namespace donde esté la clase extension, podemos invocar la función GetFullName sobre un objeto person.

    Person person = new Person ("Jorge","Sánchez Fernández");

    Debug.WriteLine(person.GetFullName());

Objetive-C

En Objetive-C para crearnos una categoría, el nombre de la clase debe ser nombreclaseaextender (nombrecategoria), Xcode crea el nombre de fichero como nombreclaseeextender+nombrecategoria.h y nombreclaseeextender+nombrecategoria.h.

Apple recomienda que la clase a extender y la categoría lleven un prefijo para evitar la situación de tener por ejemplo una categoría que sea NSSring(isEmpty) y que una nueva versión de Foundation por ejemplo traiga justo esa funcionalidad, entonces al existir dos categorias iguales el comportamiento puede ser inexperado. Si la clase a extender también es nuestra también debería ir prefijada.

XURPerson+XURFullNameExtension.h
#import "Person.h"

@interface XURPerson (XURPersonAditions)

-(void)getFullname;
@end

XURPerson+XURFullNameExtension.m

@implementation Person (XURPersonAditions)

-(void)getFullname
{
  return  [[NSString alloc] initWithFormat:@"%@ %@", self.firstName, self.lastName];
}
@end


Añadiendo el import de la categoría, podemos invocar la función getFullName sobre un objeto person.


    #import "XURPerson+XURFullNameExtension.h"
    .
    .
    .

    Person *person = [[Person alloc] initWithFisrtName:@"Jorge" lastName:@"Sánchez"];

    NSLog([person getFullName]);

Extensiones

Otro mecanismo para extender una clase en Objetive-C son las extensiones, para este método de extensión es necesario tener el código fuente de la clase que queremos extender porque se realiza añadiendo código en el fichero .m.

En C# no hay nada parecido ya que el uso de extensiones en Objetive-C tiene como proposito encapsular en la implementación de una clase ciertas métodos o propiedadades de la misma, es decir, todo lo que indicamos dentro de una extensión va a ser privado para la implementación de la clase. En C# existen los modificadores de acceso de miembros para variables, métodos y propiedades, de esta forma se oculta hacia fuera de la clase estas definiciones.

Para definir una extensión tenemos que añadir un @interface dentro del fichero .m, seguido del nombre de la clase y unos paréntesis, y dentro de esta sección interface en el fochero .m será lo que se entiende como extensión.

//Fichero claseEjemplo.m

@interface NombreClase()

@property (strong, nonanatomic) NSString *propiedadPrivada;

-(void) metodoPrivado;
@end

@implementation NombreClase

@end


Un uso, que según he visto, es bastante común, es denifir una propiedad de solo lectura en la cabecera y definir la misma propiedad de lectura y escritura en la extensión de la clase para utilizarla en modo escritura desde la implementación y exponerla en modo solo lectura a los clientes.

//Fichero Person.h

@interface Person

@property (strong, nonanatomic,readonly) NSString *firstName;


@end

//Fichero Person.m

@interface Person()

@property (strong, nonanatomic) NSString *firstName;
@end

@implementation Person

@end


Esto es algo que me choca bastante respecto a C# porque ya que @property crea una variable de instancia se puede utilizar dicha variable siempre desde la implementación, pero bueno he visto que es bastante habitual esta práctica en Objetive-C.

Resumen

En este artículo hemos los dos métodos de extender una clase en Objetive C, categorías donde podemos extender clases de las que no tenemos el código fuente y es similar a las extensiones de C# y por otra parte las extensiones propias de Objetive-C donde es necesario tener el código fuente y el objeto es poner en la extensión aquello que queremos que sea privado a la implementación de la clase.

Libros relacionados

iOs Programming for .Net Developers

iOS Programming: The Big Nerd Ranch Guide

Objective-C Programming: The Big Nerd Ranch Guide

No hay comentarios:

Publicar un comentario