Objective-C es un lenguaje informático simple, diseñado para admitir la programación orientada a objetos real.

Objective-C extiende el lenguaje C ANSI estándar al proporcionar sintaxis para definiciones de clases, métodos y propiedades, así como otras estructuras que pueden mejorar la capacidad de extensión dinámica de las clases. La sintaxis y el diseño de las clases se basan principalmente en Smalltalk, uno de los primeros lenguajes de programación orientada a objetos.

Si has utilizado otros lenguajes de programación orientada a objetos anteriormente, la siguiente información puede ayudarte a aprender la sintaxis básica de Objective-C. Muchos conceptos tradicionales de la orientación a objetos, como encapsulación, herencia y polimorfismo, están presentes en Objective-C. Hay algunas diferencias importantes, pero estas diferencias se mostrarán en este artículo, y si lo necesitas, también existe información más detallada.

Si nunca has programado con ningún lenguaje de programación, al menos necesitas tener un conocimiento básico de los conceptos relevantes antes de comenzar. El uso de objetos y la arquitectura de objetos es la base del diseño de programas para iPhone; comprender cómo interactúan es muy importante para crear tus programas. Para conocer los conceptos de orientación a objetos, consulta Programación orientada a objetos con Objective-C.

Objective-C: superconjunto de C

Objective-C es un superconjunto estricto de C: cualquier programa en C puede compilarse directamente con el compilador de Objective-C sin modificaciones, y es totalmente legal usar código C en Objective-C. Objective-C se describe como una fina capa sobre C, porque la intención original de Objective-C era añadir características de orientación a objetos al cuerpo del lenguaje C.

Extensiones de archivo del código Objective-C

Extensión Tipo de contenido
.h Archivo de cabecera. El archivo de cabecera contiene declaraciones de clases, tipos, funciones y constantes.
.m Archivo de código fuente. Esta es la extensión típica de archivo de código fuente; puede contener código Objective-C y C.
.mm Archivo de código fuente. Los archivos de código fuente con esta extensión pueden contener código C++ además de código Objective-C y C. Usa esta extensión solo cuando realmente necesites utilizar clases o características de C++ en tu código Objective-C.

Cuando necesites incluir un archivo de cabecera en el código fuente, puedes usar la directiva estándar #include, pero Objective-C ofrece un método mejor. La directiva #import es exactamente igual que #include, excepto que garantiza que el mismo archivo solo se incluya una vez. Los ejemplos y la documentación de Objective-C tienden a usar #import, y tu código también debería hacerlo.

Sintaxis

La sintaxis orientada a objetos de Objective-C proviene del estilo de paso de mensajes de Smalltalk. Toda la demás sintaxis no orientada a objetos, incluidos los tipos de variables, el preprocesador, el control de flujo, la declaración y llamada de funciones, es exactamente igual que en C. Sin embargo, algunos códigos de sintaxis legal de C no significan necesariamente lo mismo en Objective-C; por ejemplo, ciertas expresiones booleanas que en C devuelven true, si se comparan directamente con yes en Objective-C, la función fallará, porque en Objective-C el valor de yes solo se representa como 1.

El primer programa en Objective-C, basado en Xcode 4.3.1:

#import <Foundation/Foundation.h>

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

    @autoreleasepool {
        NSLog(@"Hello World!");
    }

   return 0;
}

Paso de mensajes

La característica más destacada de Objective-C es el modelo de paso de mensajes (message passing) heredado de Smalltalk, un mecanismo muy diferente del estilo dominante actual de C++. En Objective-C, más que decir que los objetos se llaman métodos entre sí, es más preciso decir que los objetos se envían mensajes entre sí. La principal diferencia entre estos dos estilos radica en la acción de llamar al método / pasar el mensaje. En C++, la relación entre clase y método es estricta y clara: un método pertenece necesariamente a una clase y está estrechamente vinculado en tiempo de compilación; es imposible llamar a un método que no existe en una clase. Pero en Objective-C, la relación entre clases y mensajes es más flexible; llamar a un método se considera enviar un mensaje al objeto, y todos los métodos se consideran respuestas al mensaje. Todo el procesamiento de mensajes se decide dinámicamente en tiempo de ejecución (runtime), y se deja a la clase decidir cómo manejar el mensaje recibido. Es decir, una clase no garantiza que responderá al mensaje recibido; si la clase recibe un mensaje que no puede manejar, el programa solo lanzará una excepción, no se producirá un error o un fallo.

En C++, la sintaxis para enviar un mensaje a un objeto (o llamar a un método) es la siguiente:

obj.method(argument);

En Objective-C se escribe así:

[obj method: argument];

Estos dos no solo difieren en sintaxis, sino también en comportamiento básico.

Aquí se usa un ejemplo simple de una clase de automóvil (car class) para explicar la característica de paso de mensajes de Objective-C:

[car fly];

La interpretación típica en C++ es "llamar al método fly de la clase car". Si la clase car no tiene definido el método fly, la compilación no pasará. Pero en Objective-C, debemos interpretarlo como "enviar un mensaje fly al objeto car"; fly es el mensaje y car es el receptor del mensaje. Después de recibir el mensaje, car decidirá cómo responder a este mensaje; si la clase car tiene definido el método fly, se ejecutará el código de ese método; si el método fly no existe en car, el programa aún puede compilar, pero en tiempo de ejecución se lanzará una excepción.

Ambos estilos tienen ventajas y desventajas. C++ exige que todos los métodos tengan una acción correspondiente, y la vinculación en tiempo de compilación hace que las llamadas a funciones sean muy rápidas. La desventaja es que solo puede ofrecer una capacidad de vinculación dinámica limitada mediante la palabra clave virtual. Objective-C posee de forma innata la capacidad de vinculación dinámica de tipado pato (duck typing), porque los mensajes se procesan en tiempo de ejecución, lo que permite enviar mensajes desconocidos a los objetos. Se pueden enviar mensajes a toda una colección de objetos sin necesidad de verificar el tipo de cada objeto uno por uno, y también dispone de un mecanismo de reenvío de mensajes. Además, el objeto nulo nil, después de recibir un mensaje, por defecto no hace nada, por lo que enviar mensajes a nil tampoco causa que el programa se bloquee.

Cadenas

Como superconjunto de C, Objective-C admite las convenciones de cadenas del lenguaje C. Es decir, los caracteres individuales se encierran entre comillas simples y las cadenas se encierran entre comillas dobles. Sin embargo, la mayoría de Objective-C generalmente no usa cadenas al estilo de C. En cambio, la mayoría de los frameworks pasan cadenas a objetos NSString. La clase NSString proporciona un envoltorio de clase para cadenas, que incluye todas las ventajas que se esperan, como un mecanismo incorporado de gestión de memoria para almacenar cadenas de cualquier longitud, soporte para Unicode, herramientas de formato de estilo printf, etc. Debido a que este tipo de cadena se usa con mucha frecuencia, Objective-C proporciona un atajo para crear objetos NSString fácilmente a partir de valores constantes. Para usar este atajo, todo lo que necesitas hacer es colocar un símbolo @ delante de la cadena entre comillas dobles normal, como se muestra en el siguiente ejemplo:

NSString* myString = @"My String\n";
NSString* anotherString = [NSString stringWithFormat:@"%d %s", 1, @"String"];

// 从一个C语言字符串创建Objective-C字符串
NSString*  fromCString = [NSString stringWithCString:"A C string" 
encoding:NSASCIIStringEncoding];

Clase

Como en todos los demás lenguajes orientados a objetos, la clase es la estructura básica que Objective-C utiliza para encapsular datos y el comportamiento que opera sobre esos datos. Un objeto es una instancia de la clase en tiempo de ejecución; contiene su propia copia de memoria de las variables de instancia declaradas por la clase, así como punteros a los miembros de la clase. La especificación de clase de Objective-C incluye dos partes: la definición (interface) y la implementación (implementation). La parte de definición (interface) contiene la declaración de la clase, la definición de las variables de instancia y los métodos relacionados con la clase. La parte de implementación (implementation) contiene el código real de los métodos de la clase.

La siguiente figura muestra la sintaxis para declarar una clase llamada MyClass, que hereda de la clase base NSObject. La declaración de clase siempre comienza con la directiva @interface y termina con la directiva @end. Después del nombre de la clase (separado por dos puntos) está el nombre de la clase padre. Las variables de instancia (o miembro) de la clase se declaran en el bloque de código encerrado entre llaves. Después del bloque de variables de instancia está la lista de métodos declarados por la clase. Cada declaración de variable de instancia y método termina con un punto y coma.

Los archivos de definición de clase siguen la convención del lenguaje C: la extensión .h para la interfaz y la extensión .m para la implementación.

Diagrama de declaración de clase

Interface

La parte de definición define claramente el nombre de la clase, los miembros de datos y los métodos. Comienza con la palabra clave @interface y termina con @end.

@interface MyObject : NSObject {
    int memberVar1; // 实体变量
    id  memberVar2;
}

+(return_type) class_method; // 类方法

-(return_type) instance_method1; // 实例方法
-(return_type) instance_method2: (int) p1;
-(return_type) instance_method3: (int) p1 andPar: (int) p2;
@end

El signo +/- delante del método representa el tipo de función: el signo más (+) representa un método de clase (class method), que se puede llamar sin una instancia, similar a la función estática (static member function) de C++. El signo menos (-) es un método de instancia (instance method) normal.

Aquí se proporciona una comparación con sintaxis de C++ de significado similar, de la siguiente manera:

class MyObject : public NSObject {
protected:
    int memberVar1;  // 实体变量
    void * memberVar2;

  public:
    static return_type class_method(); // 類方法

    return_type instance_method1();    // 实例方法
    return_type instance_method2( int p1 );
    return_type instance_method3( int p1, int p2 );
}

Cuando Objective-C define un nuevo método, los dos puntos (:) en el nombre representan el paso de parámetros, a diferencia de C, que usa paréntesis de funciones matemáticas para pasar parámetros. Los métodos de Objective-C permiten que los parámetros se intercalen en medio del nombre, sin tener que adjuntarlos todos al final del nombre del método, lo que puede mejorar la legibilidad del programa. Tomemos como ejemplo el método para establecer los valores RGB de un color:

- (void) setColorToRed: (float)red Green: (float)green Blue:(float)blue; /* 宣告方法*/

[myColor setColorToRed: 1.0 Green: 0.8 Blue: 0.2]; /* 呼叫方法*/

La firma de este método es setColorToRed:Green:Blue:. Después de cada dos puntos hay un parámetro de tipo float, que representa respectivamente los colores rojo, verde y azul.

Implementation

El bloque de implementación contiene la implementación de los métodos públicos, así como la definición de variables y métodos privados. Comienza con la palabra clave @implementation y termina con @end.

@implementation MyObject {
  int memberVar3; //私有實體變數
}

+(return_type) class_method {
    .... //method implementation
}
-(return_type) instance_method1 {
     ....
}
-(return_type) instance_method2: (int) p1 {
    ....
}
-(return_type) instance_method3: (int) p1 andPar: (int) p2 {
    ....
}
@end

Cabe mencionar que no solo el bloque Interface puede definir variables de instancia; el bloque Implementation también puede definirlas. La diferencia entre ambos radica en los permisos de acceso: las variables de instancia en el bloque Interface tienen por defecto el permiso protected, mientras que las declaradas en el bloque implementation son private por defecto. Por lo tanto, definir miembros privados en el bloque Implementation se ajusta mejor al principio de encapsulamiento de la programación orientada a objetos, ya que así la información privada de la clase no necesita exponerse en la interfaz pública (archivo .h).

Creación de objetos

En Objective-C, la creación de objetos requiere dos mensajes: alloc e init. alloc sirve para asignar memoria, mientras que init inicializa el objeto. Tanto init como alloc son métodos definidos en NSObject. Una vez que el objeto padre recibe estos dos mensajes y responde correctamente, el nuevo objeto queda creado. A continuación se muestra un ejemplo:

MyObject * my = [[MyObject alloc] init];

En Objective-C 2.0, si la creación del objeto no requiere parámetros, se puede usar directamente new.

MyObject * my = [MyObject new];

Es solo una simplificación sintáctica; el efecto es exactamente el mismo.

Si se desea personalizar el proceso de inicialización, se puede sobrescribir el método init para añadir trabajo adicional. (Su propósito es similar al constructor de C++).

Métodos

Las clases en Objective-C pueden declarar dos tipos de métodos: métodos de instancia y métodos de clase. Un método de instancia es un método que se ejecuta en el ámbito de una instancia concreta de la clase. Es decir, antes de llamar a un método de instancia, primero debes crear una instancia de la clase. Un método de clase, en cambio, no requiere que crees una instancia.

La declaración de un método incluye el identificador de tipo de método, el tipo de retorno, una o más palabras clave de identificación del método, y la información de tipo y nombre de los parámetros. La siguiente figura muestra la declaración del método de instancia insertObject:atIndex:. La declaración comienza con un signo menos (-), lo que indica que es un método de instancia. El nombre real del método (insertObject:atIndex:) es la concatenación de todas las palabras clave de identificación del método, incluidos los dos puntos. Los dos puntos indican la presencia de parámetros. Si el método no tiene parámetros, se pueden omitir los dos puntos después de la primera (y única) palabra clave de identificación del método. En este ejemplo, el método tiene dos parámetros.

Sintaxis de declaración de métodos

Cuando quieres invocar un método, envías un mensaje al objeto correspondiente. Aquí el mensaje es el identificador del método, junto con la información de los parámetros que se pasan al método. Todos los mensajes enviados a un objeto se distribuyen dinámicamente, lo que favorece el comportamiento polimórfico de las clases de Objective-C. Es decir, si una subclase define un método con el mismo identificador que el de su superclase, la subclase recibe primero el mensaje y luego puede reenviarlo (o no) a su superclase de forma selectiva.

Los mensajes se encierran entre corchetes ([ y ]). Dentro de los corchetes, el objeto que recibe el mensaje está a la izquierda, y el mensaje (incluidos los parámetros que necesite) está a la derecha. Por ejemplo, para enviar el mensaje insertObject:atIndex: a la variable myArray, se utiliza la siguiente sintaxis:

[myArray insertObject:anObj atIndex:0];

Para evitar declarar demasiadas variables locales que guarden resultados temporales, Objective-C permite usar mensajes anidados. El valor de retorno de cada mensaje anidado puede usarse como argumento o destino de otros mensajes. Por ejemplo, puedes reemplazar cualquiera de las variables del ejemplo anterior con cualquier mensaje que obtenga ese tipo de valor. Así, si tienes otro objeto llamado myAppObject con métodos que pueden acceder al objeto de tipo array e insertar un objeto en un array, puedes escribir el ejemplo anterior de la siguiente manera:

[[myAppObject getArray] insertObject:[myAppObject getObjectToInsert] atIndex:0];

Aunque los ejemplos anteriores envían mensajes a una instancia de una clase, también puedes enviar mensajes a la propia clase. Al enviar un mensaje a una clase, el método que especifiques debe estar definido como método de clase, no como método de instancia. Puedes considerar que los métodos de clase se parecen un poco a los miembros estáticos de las clases de C++ (aunque no son exactamente iguales).

El uso típico de los métodos de clase es como métodos de fábrica para crear nuevas instancias de la clase, o como vía para acceder a información compartida relacionada con la clase. La sintaxis de declaración de un método de clase es casi idéntica a la de un método de instancia, con una pequeña diferencia: mientras que el método de instancia usa el signo menos como identificador de tipo de método, el método de clase usa el signo más (+).

El siguiente ejemplo demuestra cómo un método de clase puede actuar como método de fábrica de la clase. Aquí, arrayWithCapacity es un método de clase de la clase NSMutableArray, que asigna e inicializa el contenido de una nueva instancia de la clase y luego te la devuelve.

NSMutableArray*   myArray = nil; // nil 基本上等同于 NULL

// 创建一个新的数组,并把它赋值给 myArray 变量
myArray = [NSMutableArray arrayWithCapacity:0];

Propiedades

Las propiedades son una forma conveniente de sustituir la declaración de métodos de acceso. Las propiedades no crean una nueva variable de instancia en la declaración de tu clase; son simplemente una notación abreviada para definir métodos que acceden a variables de instancia ya existentes. Una clase que exponga variables de instancia puede usar la notación de propiedades en lugar de la sintaxis de getters y setters. Las clases también pueden usar propiedades para exponer variables de instancia "virtuales", que son el resultado del cálculo dinámico de algunos datos y no están realmente almacenadas en una variable de instancia.

De hecho, se puede decir que las propiedades te ahorran una gran cantidad de código redundante que de otro modo tendrías que escribir. Dado que la mayoría de los métodos de acceso se implementan de forma similar, las propiedades evitan la necesidad de proporcionar getters y setters distintos para cada variable de instancia expuesta por la clase. En su lugar, especificas el comportamiento deseado con la declaración de propiedad y, durante la compilación, se sintetizan los métodos getter y setter reales basados en esa declaración.

Las declaraciones de propiedades deben colocarse junto a las declaraciones de métodos en la interfaz de la clase. La definición básica utiliza la directiva de compilación @property, seguida de la información de tipo y el nombre de la propiedad. También puedes configurar la propiedad con opciones personalizadas, que determinan el comportamiento de los métodos de acceso. El siguiente ejemplo muestra algunas declaraciones de propiedades simples:

@interface Person : NSObject {
    @public
        NSString *name;
    @private
        int age;
}

@property(copy) NSString *name;
@property(readonly) int age;

-(id)initWithAge:(int)age;
@end

Los métodos de acceso de las propiedades se implementan mediante la palabra clave @synthesize, que genera automáticamente un par de métodos de acceso a partir de la declaración de la propiedad. Alternativamente, se puede usar la palabra clave @dynamic para indicar que los métodos de acceso serán proporcionados manualmente por el programador.

@implementation Person
@synthesize name;
@dynamic age;

-(id)initWithAge:(int)initAge
{
    age = initAge; // 注意:直接赋给成员变量,而非属性
    return self;
}

-(int)age
{
    return 29; // 注意:并非返回真正的年龄
}
@end

Las propiedades pueden accederse mediante expresiones de mensaje tradicionales, expresiones de punto o los pares de métodos "valueForKey:"/"setValue:forKey:".

Person *aPerson = [[Person alloc] initWithAge: 53];
aPerson.name = @"Steve"; // 注意:点表达式,等于[aPerson setName: @"Steve"];
NSLog(@"Access by message (%@), dot notation(%@), property name(%@) and direct instance variable access (%@)",
      [aPerson name], aPerson.name, [aPerson valueForKey:@"name"], aPerson->name);

Para acceder a las propiedades de la instancia mediante la expresión de punto, es necesario usar la palabra clave "self":

-(void) introduceMyselfWithProperties:(BOOL)useGetter
{
    NSLog(@"Hi, my name is %@.", (useGetter ? self.name : name)); // NOTE: getter vs. ivar access
}

Las propiedades de una clase o protocolo pueden leerse dinámicamente.

int i;
int propertyCount = 0;
objc_property_t *propertyList = class_copyPropertyList([aPerson class], &propertyCount);

for ( i=0; i < propertyCount; i++ ) {
    objc_property_t *thisProperty = propertyList + i;
    const char* propertyName = property_getName(*thisProperty);
    NSLog(@"Person has a property: '%s'", propertyName);
}

Enumeración rápida

En lugar de utilizar objetos NSEnumerator o enumerar secuencialmente en una colección, Objective-C 2.0 proporciona una sintaxis de enumeración rápida. En Objective-C 2.0, los siguientes bucles son funcionalmente equivalentes, pero tienen diferentes características de rendimiento.

// 使用NSEnumerator
NSEnumerator *enumerator = [thePeople objectEnumerator];
Person *p;

while ( (p = [enumerator nextObject]) != nil ) {
    NSLog(@"%@ is %i years old.", [p name], [p age]);
}
// 使用依次枚举
for ( int i = 0; i < [thePeople count]; i++ ) {
    Person *p = [thePeople objectAtIndex:i];
    NSLog(@"%@ is %i years old.", [p name], [p age]);
}
// 使用快速枚举
for (Person *p in thePeople) {
    NSLog(@"%@ is %i years old.", [p name], [p age]);
}

La enumeración rápida puede producir código más eficiente que la enumeración estándar, ya que los métodos que invoca la enumeración son reemplazados por operaciones aritméticas de punteros proporcionadas por el protocolo NSFastEnumeration.


Protocolos (Protocol)

Un protocolo es una lista de métodos sin implementar; cualquier clase puede adoptar el protocolo e implementar concretamente ese conjunto de métodos.

Objective-C intentó introducir el concepto de herencia múltiple en la era de NeXT, pero no lo implementó debido a la aparición de los protocolos.

Los protocolos son similares a las "interfaces" en los lenguajes Java y C#. En Objective-C hay dos formas de definir protocolos: los "protocolos formales", garantizados por el compilador, y los "protocolos informales", establecidos para propósitos específicos.

Protocolos informalesEs una lista de métodos que pueden implementarse de forma selectiva. Aunque se llaman protocolos, los protocolos informales son en realidad una denominación para una categoría no implementada (Unimplemented Category) colgada de NSObject; en el mecanismo del lenguaje Objective-C no existe tal cosa como un protocolo informal. Después de OS X 10.6, con la introducción de la palabra clave @optional, los protocolos formales adquirieron la misma capacidad, por lo que los protocolos informales han quedado en desuso y ya no se utilizan.

Protocolos formalesEs similar a una "interfaz" en Java; es una lista de métodos, y cualquier clase puede declarar que implementa un protocolo determinado. Antes de Objective-C 2.0, una clase debía implementar todos los métodos del protocolo que declaraba cumplir; de lo contrario, el compilador informaba de un error indicando que la clase no implementaba todos los métodos del protocolo que decía cumplir. Objective-C 2.0 permite marcar algunos métodos de un protocolo como opcionales (Optional), de modo que el compilador no obliga a implementarlos.

Los protocolos se aplican con frecuencia en la delegación (delegate) y el disparo de eventos en Cocoa. Por ejemplo, una clase de campo de texto suele incluir un objeto delegado (delegate) que puede implementar un protocolo, el cual puede contener un método para realizar el autocompletado de la entrada de texto. Si el objeto delegado implementa ese método, la clase de campo de texto disparará el evento de autocompletado en el momento adecuado y llamará a ese método para la función de autocompletado.

El concepto de protocolo en Objective-C no es exactamente igual al de interfaz en Java. Es decir, una clase puede implementar los métodos contenidos en un protocolo sin declarar que cumple ese protocolo, lo que en la práctica equivale a cumplirlo; esta diferencia es invisible para el código externo. La declaración de un protocolo formal no proporciona implementación; simplemente indica que la clase que cumple el protocolo implementa sus métodos, asegurando que quien llama puede invocar los métodos de forma segura.

Sintaxis

Un protocolo comienza con la palabra clave @protocol como inicio del bloque, termina con @end, y en medio se encuentra la lista de métodos.

@protocol Locking
- (void)lock;
- (void)unlock;
@end

Este es un ejemplo de protocolo. En la programación con múltiples hilos, a menudo es necesario garantizar que un recurso compartido solo pueda ser utilizado por un hilo a la vez; para ello se coloca un candado al recurso antes de usarlo. Lo anterior es un protocolo que indica el concepto de "bloqueo"; el protocolo tiene dos métodos, de los cuales solo se dan los nombres pero aún no se han implementado.

La siguiente SomeClass declara que adopta el protocolo Locking:

@interface SomeClass : SomeSuperClass <Locking>
@end

Una vez que SomeClass indica que adopta el protocolo Locking, SomeClass tiene la obligación de implementar los dos métodos del protocolo Locking.

@implementation SomeClass
- (void)lock {
  // 實現lock方法...
}
- (void)unlock {
  // 實現unlock方法...
}
@end

Dado que SomeClass ya cumple efectivamente con el protocolo Locking, el llamador puede enviar de forma segura mensajes lock o unlock a una variable de instancia de SomeClass, sin preocuparse de que esta no pueda responder al mensaje.

Los complementos (plugins) son otro ejemplo de uso de definiciones abstractas; se puede definir el comportamiento deseado sin preocuparse por la implementación del complemento.


Tipos dinámicos

Al igual que Smalltalk, Objective-C posee tipado dinámico: es decir, se pueden enviar mensajes a cualquier objeto, independientemente de si la interfaz pública de dicho objeto tiene o no el método correspondiente. En comparación con lenguajes de tipado estático como C++, donde el compilador rechaza las llamadas a métodos a través de un puntero (void*), en Objective-C puedes enviar cualquier mensaje a un id (id es muy parecido a void*, pero está estrictamente limitado a usarse solo con objetos); el compilador únicamente emite la advertencia de que "el objeto puede no responder al mensaje", el programa puede compilar, y lo que realmente ocurre depende de la forma real del objeto en tiempo de ejecución. Si el objeto efectivamente puede responder al mensaje, se ejecuta el método correspondiente.

Después de que un objeto recibe un mensaje, tiene tres posibles formas de manejarlo: la primera es responder al mensaje y ejecutar el método; si no puede responder, puede reenviar el mensaje a otro objeto; si no ocurre ninguna de las anteriores, debe manejar la excepción lanzada por no poder responder. Con tal de realizar una de estas tres acciones, el mensaje se considera cumplido y se descarta. Si se envía un mensaje a "nil" (puntero a objeto nulo), dicho mensaje normalmente se ignora, aunque dependiendo de las opciones del compilador puede lanzar una excepción.

Aunque Objective-C tiene capacidad de tipado dinámico, la comprobación estática de tipos en tiempo de compilación aún puede aplicarse a las variables. Las tres declaraciones siguientes tienen exactamente el mismo efecto en tiempo de ejecución, pero cada una proporciona información de tipo más explícita que la anterior. La información de tipo adicional permite al compilador verificar el tipo de las variables durante la compilación y advertir sobre variables cuyo tipo no coincide.

Los tres métodos siguientes difieren únicamente en la forma de los parámetros:

- setMyValue:(id) foo;

La forma id indica que el parámetro "foo" puede ser una instancia de cualquier clase.

- setMyValue:(id <aProtocol>) foo;

id<aProtocol> indica que "foo" puede ser una instancia de cualquier clase, pero debe adoptar el protocolo "aProtocol".

- setMyValue:(NSNumber*) foo;

Esta declaración indica que "foo" debe ser una instancia de "NSNumber".

El tipado dinámico es una característica poderosa. Al implementar clases contenedoras en lenguajes de tipado estático que carecen de genéricos (como las versiones anteriores a Java 5), el programador necesita escribir una clase contenedora para objetos de un tipo genérico y luego realizar conversiones de tipo (casts) constantemente entre el tipo genérico y el tipo real. De cualquier modo, las conversiones de tipo rompen el tipado estático; por ejemplo, escribir un "entero" y leerlo como "cadena" produce un error en tiempo de ejecución. Este problema se resuelve con los genéricos, pero las clases contenedoras requieren que los objetos de su contenido tengan un tipo consistente, mientras que en los lenguajes de tipado dinámico no existe este problema en absoluto.


Reenvío

Objective-C permite enviar un mensaje a un objeto, sin importar si este puede responderlo. Además de responder o descartar el mensaje, el objeto también puede reenviarlo a otro objeto que pueda responderlo. El reenvío puede utilizarse para simplificar ciertos patrones de diseño, como el patrón observador o el patrón proxy.

El runtime de Objective-C define un par de métodos en Object:

Métodos de reenvío:

- (retval_t) forward:(SEL) sel :(arglist_t) args; // with GCC
- (id) forward:(SEL) sel :(marg_list) args; // with NeXT/Apple systems

Métodos de respuesta:

- (retval_t) performv:(SEL) sel :(arglist_t) args;  // with GCC
- (id) performv:(SEL) sel :(marg_list) args; // with NeXT/Apple systems

Los objetos que deseen implementar el reenvío solo necesitan sobrescribir el método anterior con un nuevo método para definir su comportamiento de reenvío. No es necesario reescribir el método de respuesta performv::, ya que este método simplemente envía un mensaje y pasa los parámetros al objeto que responde. Aquí, el tipo SEL es el tipo de los mensajes en Objective-C.

El siguiente código demuestra el concepto básico del reenvío:

Código del archivo Forwarder.h:

#import <objc/Object.h>

@interface Forwarder : Object
{
    id recipient; //该对象是我们希望转发到的对象。
}

@property (assign, nonatomic) id recipient;

@end

Código del archivo Forwarder.m:

#import "Forwarder.h"

@implementation Forwarder

@synthesize recipient;

- (retval_t) forward: (SEL) sel : (arglist_t) args
{
    /*
     *检查转发对象是否响应该消息。
     *若转发对象不响应该消息,则不会转发,而产生一个错误。
     */
    if([recipient respondsTo:sel])
       return [recipient performv: sel : args];
    else
       return [self error:"Recipient does not respond"];
}

Código del archivo Recipient.h:

#import <objc/Object.h>

// A simple Recipient object.
@interface Recipient : Object
- (id) hello;
@end

Código del archivo Recipient.m:

#import "Recipient.h"

@implementation Recipient

- (id) hello
{
    printf("Recipient says hello!\n");

    return self;
}

@end

Código del archivo main.m:

#import "Forwarder.h"
#import "Recipient.h"

int main(void)
{
    Forwarder *forwarder = [Forwarder new];
    Recipient *recipient = [Recipient new];

    forwarder.recipient = recipient; //Set the recipient.
    /*
     *转发者不响应hello消息!该消息将被转发到转发对象。
     *(若转发对象响应该消息)
     */
    [forwarder hello];

    return 0;
}

Al compilar con GCC, el compilador informa:

$ gcc -x objective-c -Wno-import Forwarder.m Recipient.m main.m -lobjc
main.m: In function `main':
main.m:12: warning: `Forwarder' does not respond to `hello'
$

Como se mencionó anteriormente, el compilador informa que la clase Forwarder no responde al mensaje hello. En este caso, como se ha implementado el reenvío, se puede ignorar esta advertencia. Al ejecutar el programa se obtiene la siguiente salida:

$ ./a.out
Recipient says hello!

Categorías (Category)

En el diseño de Objective-C, una consideración principal es el mantenimiento de grandes marcos de código. La experiencia de la programación estructurada muestra que una de las principales formas de mejorar el código es dividirlo en fragmentos más pequeños. Objective-C toma prestado y extiende el concepto de "categoría" de la implementación de Smalltalk para ayudar a lograr este propósito de descomposición del código.

Una categoría puede dividir la implementación de métodos en una serie de archivos separados. El programador puede agrupar un conjunto de métodos relacionados en una categoría, haciendo que el programa sea más legible. Por ejemplo, se puede añadir una categoría llamada "revisión ortográfica" a la clase de cadenas y colocar el código relacionado con la revisión ortográfica en dicha categoría.

Además, los métodos de una categoría se añaden a la clase en tiempo de ejecución; esta característica permite al programador agregar métodos a clases existentes sin necesidad de disponer del código original ni de recompilar la clase original. Por ejemplo, si la implementación de la clase de cadenas proporcionada por el sistema no incluye la funcionalidad de revisión ortográfica, se puede añadir dicha funcionalidad sin cambiar el código de la clase de cadenas original.

En tiempo de ejecución, los métodos de una categoría no se diferencian de los métodos originales de la clase; su código puede acceder a todas las variables miembro, incluidas las variables miembro privadas de la clase.

Si una categoría declara un método con el mismo nombre que un método existente en la clase, se llamará al método de la categoría. Por lo tanto, una categoría no solo puede añadir métodos a la clase, sino también reemplazar métodos existentes. Esta característica se puede utilizar para corregir errores en el código original e incluso para cambiar fundamentalmente el comportamiento de la clase original en el programa. Si dos categorías tienen métodos con el mismo nombre, el método que se invoca es impredecible.

Otros lenguajes también han intentado añadir esta característica mediante distintos métodos. TOM va más lejos en este aspecto: no solo permite añadir métodos, sino también variables miembro. También hay otros lenguajes que utilizan soluciones orientadas a declaraciones, entre las que destaca especialmente el lenguaje Self.

C# y Visual Basic .NET implementan una funcionalidad similar mediante métodos de extensión y clases parciales. Ruby y algunos lenguajes dinámicos denominan a esta técnica "monkey patch".

Ejemplo de uso de categorías

Este ejemplo crea la clase Integer, que por sí misma solo define la propiedad integer, y luego añade dos categorías, Arithmetic y Display, para extender la funcionalidad de la clase. Aunque una categoría puede acceder a los miembros privados de la clase, normalmente es mejor práctica utilizar los métodos de acceso a las propiedades, lo que hace que la categoría sea más independiente de la clase original. Este es un uso típico de las categorías; otro uso es reemplazar métodos de la clase original mediante categorías, aunque no se considera una buena práctica sustituir métodos con categorías en lugar de con herencia.

Código del archivo Integer.h:

#import <objc/Object.h>

@interface Integer : Object
{
@private
    int integer;
}

@property (assign, nonatomic) integer;

@end

Código del archivo Integer.m:

#import "Integer.h"

@implementation Integer

@synthesize integer;

@end

Código del archivo Arithmetic.h:

#import "Integer.h"

@interface Integer(Arithmetic)
- (id) add: (Integer *) addend;
- (id) sub: (Integer *) subtrahend;
@end

Código del archivo Arithmetic.m:

#import "Arithmetic.h"

@implementation Integer(Arithmetic)
- (id) add: (Integer *) addend
{
    self.integer = self.integer + addend.integer;
    return self;
}

- (id) sub: (Integer *) subtrahend
{
    self.integer = self.integer - subtrahend.integer;
    return self;
}
@end

Código del archivo Display.h:

#import "Integer.h"

@interface Integer(Display)
- (id) showstars;
- (id) showint;
@end

Código del archivo Display.m:

#import "Display.h"

@implementation Integer(Display)
- (id) showstars
{
    int i, x = self.integer;
    for(i=0; i < x; i++)
       printf("*");
    printf("\n");

    return self;
}

- (id) showint
{
    printf("%d\n", self.integer);

    return self;
}
@end

Código del archivo main.m:

#import "Integer.h"
#import "Arithmetic.h"
#import "Display.h"

int
main(void)
{
    Integer *num1 = [Integer new], *num2 = [Integer new];
    int x;

    printf("Enter an integer: ");
    scanf("%d", &x);

    num1.integer = x;
    [num1 showstars];

    printf("Enter an integer: ");
    scanf("%d", &x);

    num2.integer = x;
    [num2 showstars];

    [num1 add:num2];
    [num1 showint];

    return 0;
}

Compile con el siguiente comando:

gcc -x objective-c main.m Integer.m Arithmetic.m Display.m -lobjc

Al compilar, se puede experimentar omitiendo `#import "Arithmetic.h"` y la sentencia `[num1 add:num2]`, así como el archivo Arithmetic.m. El programa seguirá ejecutándose, lo que demuestra que se permite la carga dinámica y bajo demanda de categorías; si no se necesita la funcionalidad que proporciona una categoría, simplemente se puede optar por no compilarla.


Recolección de basura

Objective-C 2.0 ofrece un recolector de basura opcional. En el modo de compatibilidad hacia atrás, el runtime de Objective-C convierte las operaciones de conteo de referencias, como "retain" y "release", en operaciones nulas (no-op). Cuando la recolección de basura está habilitada, todos los objetos son objetos de trabajo del recolector. Los punteros C normales pueden modificarse con "__strong" para marcar que el objeto al que apunta el puntero sigue en uso. Los punteros marcados como "__weak" no se contabilizan en el recuento del recolector y se reescriben como "nil" cuando el objeto es recolectado. La implementación de Objective-C 2.0 en iOS no incluye recolección de basura. El recolector de basura se ejecuta en un hilo de fondo de baja prioridad y puede pausarse durante las acciones del usuario, manteniendo así una buena experiencia de usuario.

Enlace de referencia: https://zh.wikipedia.org/wiki/Objective-C