Objective-C es un lenguaje de programación orientado a objetos de propósito general de alto nivel que agrega el paso de mensajes (mensajería) al estilo Smalltalk al lenguaje de programación C [3] . Originalmente desarrollado por Brad Cox y Tom Love a principios de la década de 1980, fue seleccionado por NeXT para su sistema operativo NeXTSTEP . Debido al linaje directo de Apple macOS de NeXTSTEP, [4] Objective-C fue el lenguaje estándar utilizado, respaldado y promovido por Apple para desarrollar aplicaciones macOS e iOS (a través de sus respectivas interfaces de programación de aplicaciones ( API ), Cocoa y Cocoa Touch ) desde 1997, cuando Apple compró NeXT hasta la introducción del lenguaje Swift en 2014. [3]
Los programas Objective-C desarrollados para sistemas operativos que no sean de Apple o que no dependan de las API de Apple también pueden compilarse para cualquier plataforma compatible con GNU GNU Compiler Collection (GCC) o LLVM / Clang .
Los archivos de programas de 'mensajería/implementación' del código fuente de Objective-C generalmente tienen .m
extensiones de nombre de archivo, mientras que los archivos de 'encabezado/interfaz' de Objective-C tienen .h
extensiones, al igual que los archivos de encabezado de C. Los archivos Objective-C++ se denotan con una .mm
extensión de archivo.
Objective-C fue creado principalmente por Brad Cox y Tom Love a principios de la década de 1980 en su empresa Productivity Products International (PPI) . [5]
Antes de la creación de su empresa, ambos habían conocido Smalltalk mientras trabajaban en el Centro de Tecnología de Programación de ITT Corporation en 1981. Los primeros trabajos sobre Objective-C se remontan a esa época. [6] Cox estaba intrigado por los problemas de reutilización real en el diseño y la programación de software. Se dio cuenta de que un lenguaje como Smalltalk sería invaluable para crear entornos de desarrollo para desarrolladores de sistemas en ITT. Sin embargo, él y Tom Love también reconocieron que la compatibilidad con versiones anteriores de C era de vital importancia en el entorno de ingeniería de telecomunicaciones de ITT. [7]
Cox comenzó a escribir un preprocesador para C para añadir algunas de las capacidades de Smalltalk . Pronto tuvo una implementación funcional de una extensión orientada a objetos para el lenguaje C , a la que llamó Object-Oriented Pre-Compiler (OOPC). [8] Love fue contratado por Schlumberger Research en 1982 y tuvo la oportunidad de adquirir la primera copia comercial de Smalltalk-80, lo que influyó aún más en el desarrollo de su idea original. Para demostrar que se podía lograr un progreso real, Cox demostró que hacer componentes de software intercambiables realmente necesitaba solo unos pocos cambios prácticos en las herramientas existentes. Específicamente, necesitaban soportar objetos de manera flexible, venir provistos de un conjunto utilizable de bibliotecas y permitir que el código (y cualquier recurso necesario para el código) se agrupara en un formato multiplataforma .
Love y Cox finalmente formaron PPI para comercializar su producto, que combinaba un compilador Objective-C con bibliotecas de clases. En 1986, Cox publicó la descripción principal de Objective-C en su forma original en el libro Object-Oriented Programming, An Evolutionary Approach . Aunque tuvo cuidado de explicar que el problema de la reutilización implica más que lo que Objective-C proporciona, el lenguaje a menudo se comparaba característica por característica con otros lenguajes.
En 1988, NeXT obtuvo la licencia de Objective-C de StepStone (el nuevo nombre de PPI, el propietario de la marca registrada Objective-C) y amplió el compilador GCC para que fuera compatible con Objective-C. NeXT desarrolló las bibliotecas Application Kit (AppKit) y Foundation Kit en las que se basaron la interfaz de usuario NeXTSTEP y el Interface Builder. Si bien las estaciones de trabajo NeXT no lograron tener un gran impacto en el mercado, las herramientas fueron ampliamente elogiadas en la industria. NeXT abandonó la producción de hardware y se centró en herramientas de software, vendiendo NeXTSTEP (y OPENSTEP) como una plataforma para programación personalizada.
Para eludir los términos de la GPL , NeXT originalmente tenía la intención de distribuir la interfaz de Objective-C por separado, lo que permitiría al usuario vincularla con GCC para producir el ejecutable del compilador. Aunque inicialmente fue aceptado por Richard M. Stallman , este plan fue rechazado después de que Stallman consultara con los abogados de GNU y NeXT aceptara hacer de Objective-C parte de GCC. [9]
El trabajo para ampliar GNU Compiler Collection (GCC) fue dirigido por Steve Naroff, quien se unió a NeXT desde StepStone. Los cambios del compilador se pusieron a disposición según los términos de la Licencia Pública General GNU (GPL), pero las bibliotecas de ejecución no, lo que hizo que la contribución de código abierto no pudiera ser utilizada por el público en general. Esto llevó a otras partes a desarrollar dichas bibliotecas de ejecución bajo licencias de código abierto. Más tarde, Steve Naroff también fue el principal colaborador en Apple para construir la interfaz Objective-C para Clang .
El proyecto GNU comenzó a trabajar en su implementación de software libre de Cocoa , llamada GNUstep , basada en el estándar OpenStep . [10] Dennis Glatting escribió el primer entorno de ejecución GNU Objective-C en 1992. El entorno de ejecución GNU Objective-C actual, en uso desde 1993, es el desarrollado por Kresten Krab Thorup mientras era estudiante universitario en Dinamarca . [ cita requerida ] Thorup también trabajó en NeXT de 1993 a 1996. [11]
Después de adquirir NeXT en 1996, Apple Computer utilizó OpenStep en su entonces nuevo sistema operativo, Mac OS X. Esto incluía Objective-C, la herramienta de desarrollo basada en Objective-C de NeXT, Project Builder , y su herramienta de diseño de interfaz, Interface Builder . Ambas se fusionaron más tarde en una sola aplicación, Xcode . La mayor parte de la API Cocoa actual de Apple se basa en objetos de interfaz OpenStep y es el entorno Objective-C más importante que se utiliza para el desarrollo activo.
En la WWDC 2014, Apple presentó un nuevo lenguaje, Swift , que se caracterizó como "Objective-C sin C".
Objective-C es una capa fina sobre C y es un " superconjunto estricto " de C, lo que significa que es posible compilar cualquier programa en C con un compilador Objective-C e incluir libremente código de lenguaje C dentro de una clase Objective-C. [12] [13] [14] [15] [16] [17]
Objective-C deriva su sintaxis de objetos de Smalltalk . Toda la sintaxis para operaciones no orientadas a objetos (incluidas las variables primitivas, el preprocesamiento, las expresiones, las declaraciones de funciones y las llamadas de funciones) son idénticas a las de C, mientras que la sintaxis para las funciones orientadas a objetos es una implementación de la mensajería de estilo Smalltalk.
El modelo Objective-C de programación orientada a objetos se basa en el paso de mensajes a instancias de objetos. En Objective-C no se llama a un método , se envía un mensaje . Esto es diferente del modelo de programación de estilo Simula utilizado por C++ . La diferencia entre estos dos conceptos está en cómo se ejecuta el código al que hace referencia el nombre del método o del mensaje. En un lenguaje de estilo Simula, el nombre del método está en la mayoría de los casos ligado a una sección de código en la clase de destino por el compilador. En Smalltalk y Objective-C, el destino de un mensaje se resuelve en tiempo de ejecución, con el objeto receptor mismo interpretando el mensaje. Un método se identifica mediante un selector o SEL (un identificador único para cada nombre de mensaje, a menudo solo una NUL
cadena terminada en - que representa su nombre) y se resuelve en un puntero de método C que lo implementa: un IMP. [18] Una consecuencia de esto es que el sistema de paso de mensajes no tiene verificación de tipos. No se garantiza que el objeto al que se dirige el mensaje (el receptor ) responda a un mensaje, y si no lo hace, lanza una excepción. [19]
Para enviar el mensaje method
al objeto señalado por el puntero obj
se requeriría el siguiente código en C++ :
obj -> método ( argumento );
En Objective-C, esto se escribe de la siguiente manera:
[ obj método : argumento ];
El compilador traduce la llamada "método" a la objc_msgSend(id self, SEL op, ...)
familia de funciones de tiempo de ejecución. Diferentes implementaciones manejan adiciones modernas como super
. [20] En las familias GNU esta función se llama objc_msg_sendv
, pero ha quedado obsoleta en favor de un sistema de búsqueda moderno bajo objc_msg_lookup
. [21]
Ambos estilos de programación tienen múltiples fortalezas y debilidades. La programación orientada a objetos en el estilo Simula ( C++ ) permite la herencia múltiple y una ejecución más rápida mediante el uso de enlaces en tiempo de compilación siempre que sea posible, pero no admite el enlace dinámico de forma predeterminada. También obliga a todos los métodos a tener una implementación correspondiente a menos que sean abstractos . La programación de estilo Smalltalk que se usa en Objective-C permite que los mensajes no se implementen, y el método se resuelve en su implementación en tiempo de ejecución. Por ejemplo, se puede enviar un mensaje a una colección de objetos, a los que se espera que solo respondan algunos, sin temor a producir errores en tiempo de ejecución. El paso de mensajes tampoco requiere que se defina un objeto en tiempo de compilación . Aún se requiere una implementación para que se llame al método en el objeto derivado. (Consulte la sección de tipado dinámico a continuación para obtener más ventajas del enlace dinámico (tardío)).
Objective-C requiere que la interfaz y la implementación de una clase se declaren por separado en bloques de código. Por convención, los desarrolladores colocan la interfaz en un archivo de encabezado y la implementación en un archivo de código. Los archivos de encabezado, normalmente con el sufijo .h, son similares a los archivos de encabezado de C, mientras que los archivos de implementación (método), normalmente con el sufijo .m, pueden ser muy similares a los archivos de código de C.
Esto es análogo a las declaraciones de clases que se utilizan en otros lenguajes orientados a objetos, como C++ o Python.
La interfaz de una clase se define normalmente en un archivo de encabezado. Una convención común es nombrar el archivo de encabezado después del nombre de la clase, por ejemplo, Ball.h
contendría la interfaz de la clase Ball
.
Una declaración de interfaz toma la forma:
@interface classname : superclassname { // variables de instancia } + classMethod1 ; + ( tipo_de_retorno ) classMethod2 ; + ( tipo_de_retorno ) classMethod3: ( tipo_param1 ) nombre_varparam1 ; - ( tipo_retorno ) método_instancia1con1parámetro: ( tipo_param1 ) nombre_var_param1 ; - ( tipo_retorno ) método2con2parámetros de instancia: ( tipo_param1 ) nombre_var_param1 nombre_llamada_param : (tipo_param2 ) nombre_var_param2 ; @fin
En lo anterior, los signos más indican métodos de clase o métodos que se pueden llamar en la clase misma (no en una instancia), y los signos menos indican métodos de instancia , que solo se pueden llamar en una instancia particular de la clase. Los métodos de clase tampoco tienen acceso a variables de instancia .
El código anterior es aproximadamente equivalente a la siguiente interfaz C++ :
clase nombre_clase : public nombre_superclase { protected : // variables de instancia público : // Funciones de clase (estáticas) static void * classMethod1 (); static return_type classMethod2 (); static return_type classMethod3 ( param1_type param1_varName ); // Funciones de instancia (miembro) return_type instanceMethod1With1Parameter ( param1_type param1_varName ); return_type instanceMethod2With2Parameters ( param1_type param1_varName , param2_type param2_varName = predeterminado ); };
Tenga en cuenta que instanceMethod2With2Parameters:param2_callName:
demuestra el entrelazado de segmentos de selector con expresiones de argumentos, para lo cual no existe un equivalente directo en C/C++.
Los tipos de retorno pueden ser cualquier tipo estándar de C , un puntero a un objeto genérico de Objective-C, un puntero a un tipo específico de objeto como NSArray*, NSImage* o NSString*, o un puntero a la clase a la que pertenece el método (tipo de instancia). El tipo de retorno predeterminado es el tipo genérico de Objective-C id
.
Los argumentos de un método comienzan con un nombre que etiqueta el argumento que forma parte del nombre del método, seguido de dos puntos y, a continuación, el tipo de argumento esperado entre paréntesis y el nombre del argumento. La etiqueta se puede omitir.
- ( void ) setRangeStart: ( int ) inicio fin: ( int ) fin ; - ( void ) importDocumentWithName: ( NSString * ) nombre withSpecifiedPreferences :( Preferencias * ) preferencias beforePage :( int ) insertPage ;
Un derivado de la definición de interfaz es la categoría , que permite agregar métodos a clases existentes. [22]
La interfaz sólo declara la interfaz de clase y no los métodos en sí: el código real se escribe en el archivo de implementación. Los archivos de implementación (métodos) normalmente tienen la extensión de archivo .m
, que originalmente significaba "mensajes". [23]
@implementation nombre_clase + ( tipo_retorno ) método_clase { // implementación } - ( tipo_retorno ) método_instancia { // implementación } @end
Los métodos se escriben utilizando sus declaraciones de interfaz. Comparando Objective-C y C:
- ( int ) método: ( int ) i { return [ self raíz_cuadrada : i ]; }
función int ( int i ) { devolver raíz_cuadrada ( i ); }
La sintaxis permite la pseudodenominación de argumentos .
- ( void ) changeColorToRed: ( float ) rojo verde: ( float ) verde azul : ( float ) azul { //... Implementación ... } // Se llama así: [ myColor changeColorToRed : 5.0 green : 2.0 blue : 6.0 ];
Las representaciones internas de un método varían entre las diferentes implementaciones de Objective-C. Si myColor es de la clase Color
, el método de instancia -changeColorToRed:green:blue:
puede tener la etiqueta interna _i_Color_changeColorToRed_green_blue
. El i
es para hacer referencia a un método de instancia, con los nombres de la clase y del método agregados y los dos puntos cambiados a guiones bajos. Como el orden de los parámetros es parte del nombre del método, no se puede cambiar para adaptarse al estilo de codificación o expresión como con los parámetros con nombre verdadero.
Sin embargo, rara vez se utilizan directamente los nombres internos de la función. Generalmente, los mensajes se convierten en llamadas de función definidas en la biblioteca de ejecución Objective-C. No se sabe necesariamente en el momento del enlace qué método se llamará porque no es necesario conocer la clase del receptor (el objeto al que se envía el mensaje) hasta el momento de la ejecución.
Una vez que se escribe una clase Objective-C, se puede crear una instancia de la misma. Para ello, primero se asigna una instancia no inicializada de la clase (un objeto) y luego se inicializa. Un objeto no es completamente funcional hasta que se hayan completado ambos pasos. Estos pasos se deben realizar con una línea de código para que nunca haya un objeto asignado que no haya pasado por la inicialización (y porque no es aconsejable conservar el resultado intermedio, ya que -init
puede devolver un objeto diferente de aquel en el que se lo llama).
Creación de instancias con el inicializador predeterminado, sin parámetros:
MiObjeto * foo = [[ MiObjeto alloc ] init ];
Instanciación con un inicializador personalizado:
MiObjeto * foo = [[ MiObjeto alloc ] initWithString : miCadena ];
En el caso en que no se realice ninguna inicialización personalizada, a menudo se puede utilizar el método "nuevo" en lugar de los mensajes alloc-init:
MiObjeto * foo = [ MiObjeto nuevo ];
Además, algunas clases implementan inicializadores de métodos de clase. Como +new
, combinan +alloc
y -init
, pero a diferencia de +new
, devuelven una instancia liberada automáticamente. Algunos inicializadores de métodos de clase toman parámetros:
MiObjeto * foo = [ MiObjeto objeto ]; MiObjeto * bar = [ MiObjeto objetoWithString : @"Wikipedia :)" ];
El mensaje de asignación asigna suficiente memoria para contener todas las variables de instancia de un objeto, establece todas las variables de instancia en valores cero y convierte la memoria en una instancia de la clase; en ningún momento durante la inicialización la memoria es una instancia de la superclase.
El mensaje init realiza la configuración de la instancia al momento de su creación. El método init suele escribirse de la siguiente manera:
- ( id ) init { yo mismo = [ super init ]; si ( yo mismo ) { // realiza la inicialización del objeto aquí } devuelve yo mismo ; }
En el ejemplo anterior, observe el id
tipo de retorno. Este tipo representa un puntero a cualquier objeto en Objective-C (consulte la sección Tipos dinámicos).
El patrón de inicialización se utiliza para garantizar que el objeto sea inicializado correctamente por su superclase antes de que el método init realice su inicialización. Realiza las siguientes acciones:
init
mensaje a la instancia de la superclase y asigna el resultado a self
(puntero al objeto actual).Un puntero a objeto no válido tiene el valor nil
; las sentencias condicionales como if
tratan nil como un puntero nulo, por lo que el código de inicialización no se ejecutará si [super init]
se devuelve nil. Si hay un error en la inicialización, el método init debe realizar cualquier limpieza necesaria, incluido el envío de un release
mensaje a sí mismo y regresar nil
para indicar que la inicialización falló. Cualquier verificación de tales errores solo debe realizarse después de haber llamado a la inicialización de la superclase para garantizar que la destrucción del objeto se realizará correctamente.
Si una clase tiene más de un método de inicialización, solo uno de ellos (el inicializador designado ) debe seguir este patrón; los demás deben llamar al inicializador designado en lugar del inicializador de la superclase.
En otros lenguajes de programación, estas se llaman interfaces .
Objective-C se amplió en NeXT para introducir el concepto de herencia múltiple de especificaciones, pero no de implementación, mediante la introducción de protocolos . Este es un patrón que se puede lograr ya sea como una clase base abstracta de herencia múltiple en C++ o como una interfaz (como en Java y C# ). Objective-C utiliza protocolos ad hoc llamados protocolos informales y protocolos aplicados por el compilador llamados protocolos formales .
Un protocolo informal es una lista de métodos que una clase puede optar por implementar. Se especifica en la documentación, ya que no tiene presencia en el lenguaje. Los protocolos informales se implementan como una categoría (ver más abajo) en NSObject y a menudo incluyen métodos opcionales que, si se implementan, pueden cambiar el comportamiento de una clase. Por ejemplo, una clase de campo de texto puede tener un delegado que implementa un protocolo informal con un método opcional para realizar el autocompletado del texto escrito por el usuario. El campo de texto descubre si el delegado implementa ese método (a través de programación reflexiva (reflexión)) y, si es así, llama al método del delegado para admitir la función de autocompletado.
Un protocolo formal es similar a una interfaz en Java, C# y Ada 2005. Es una lista de métodos que cualquier clase puede declarar que va a implementar. Las versiones de Objective-C anteriores a la 2.0 requerían que una clase implementara todos los métodos de un protocolo que declara que adopta; el compilador emitirá un error si la clase no implementa todos los métodos de sus protocolos declarados. Objective-C 2.0 agregó compatibilidad para marcar ciertos métodos en un protocolo como opcionales, y el compilador no impondrá la implementación de métodos opcionales.
Se debe declarar una clase para implementar ese protocolo para que se pueda decir que cumple con él. Esto se puede detectar en tiempo de ejecución. Los protocolos formales no pueden proporcionar ninguna implementación; simplemente aseguran a los usuarios que llaman que las clases que cumplen con el protocolo proporcionarán implementaciones. En la biblioteca NeXT/Apple, el sistema de objetos distribuidos utiliza con frecuencia protocolos para representar las capacidades de un objeto que se ejecuta en un sistema remoto.
La sintaxis
@protocol NSLocking - ( void ) bloquear ; - ( void ) desbloquear ; @end
denota que existe la idea abstracta de bloqueo. Al indicar en la definición de clase que se implementa el protocolo,
@interface NSLock : NSObject < NSLocking > // ... @fin
Las instancias de NSLock afirman que proporcionarán una implementación para los dos métodos de instancia.
Objective-C, al igual que Smalltalk, puede utilizar tipado dinámico : se puede enviar a un objeto un mensaje que no esté especificado en su interfaz. Esto puede permitir una mayor flexibilidad, ya que permite que un objeto "capture" un mensaje y lo envíe a un objeto diferente que pueda responder al mensaje de forma adecuada, o bien enviar el mensaje a otro objeto. Este comportamiento se conoce como reenvío o delegación de mensajes (véase más abajo). Alternativamente, se puede utilizar un controlador de errores en caso de que no se pueda reenviar el mensaje. Si un objeto no reenvía un mensaje, no responde a él o no controla un error, el sistema generará una excepción en tiempo de ejecución. [24] Si se envían mensajes a nil (el puntero de objeto nulo), se ignorarán silenciosamente o generarán una excepción genérica, según las opciones del compilador.
También se puede añadir opcionalmente información de tipado estático a las variables. Esta información se comprueba en el momento de la compilación. En las cuatro sentencias siguientes, se proporciona información de tipo cada vez más específica. Las sentencias son equivalentes en el momento de la ejecución, pero la información adicional permite al compilador advertir al programador si el argumento pasado no coincide con el tipo especificado.
- ( void ) establecerMiValor: ( id ) foo ;
En la declaración anterior, foo puede ser de cualquier clase.
- ( void ) establecerMiValor: ( id < NSCopying > ) foo ;
En la declaración anterior, foo puede ser una instancia de cualquier clase que se ajuste al NSCopying
protocolo.
- ( void ) establecerMiValor: ( NSNumber * ) foo ;
En la declaración anterior, foo debe ser una instancia de la clase NSNumber .
- ( void ) establecerMiValor: ( NSNumber < NSCopying > * ) foo ;
En la declaración anterior, foo debe ser una instancia de la clase NSNumber y debe cumplir con el NSCopying
protocolo.
En Objective-C, todos los objetos se representan como punteros y no se permite la inicialización estática. El objeto más simple es el tipo al que apunta id ( objc_obj * ), que solo tiene un puntero isa que describe su clase. Otros tipos de C, como valores y estructuras, no se modifican porque no forman parte del sistema de objetos. Esta decisión difiere del modelo de objetos de C++, donde las estructuras y las clases están unidas.
Objective-C permite enviar un mensaje a un objeto que puede no responder. En lugar de responder o simplemente descartar el mensaje, un objeto puede reenviar el mensaje a un objeto que sí puede responder. El reenvío se puede utilizar para simplificar la implementación de ciertos patrones de diseño , como el patrón de observador o el patrón proxy .
El entorno de ejecución de Objective-C especifica un par de métodos enObject
- ( retval_t ) forward: ( SEL ) sel args: ( arglist_t ) args ; // con GCC - ( id ) forward: ( SEL ) sel args: ( marg_list ) args ; // con sistemas NeXT/Apple
- ( retval_t ) performv: ( SEL ) sel args: ( arglist_t ) args ; // con GCC - ( id ) performv: ( SEL ) sel args: ( marg_list ) args ; // con sistemas NeXT/Apple
Un objeto que desee implementar el reenvío solo necesita reemplazar el método de reenvío con un nuevo método para definir el comportamiento de reenvío. performv::
No es necesario reemplazar el método de acción, ya que este método simplemente realiza una acción basada en el selector y los argumentos. Observe el SEL
tipo, que es el tipo de mensajes en Objective-C.
Nota: en OpenStep, Cocoa y GNUstep, los marcos de trabajo de Objective-C más utilizados, no se utiliza la Object
clase. El método de la clase se utiliza para realizar el reenvío.- (void)forwardInvocation:(NSInvocation *)anInvocation
NSObject
A continuación se muestra un ejemplo de un programa que demuestra los conceptos básicos del reenvío.
#importar <objc/Objeto.h>@interface Forwarder : Object { id destinatario ; // El objeto al que queremos reenviar el mensaje. } // Métodos de acceso. - ( id ) destinatario ; - ( id ) setRecipient: ( id ) _recipient ; @end
#importar "Forwarder.h"@implementation Forwarder - ( retval_t ) forward: ( SEL ) sel args: ( arglist_t ) args { /* * Verifica si el destinatario realmente responde al mensaje. * Esto puede ser deseable o no, por ejemplo, si un destinatario * a su vez no responde al mensaje, podría reenviarlo * él mismo. */ if ([ receiver respondsToSelector : sel ]) { return [ receiver performv : sel args : args ]; } else { return [ self error : "El destinatario no responde" ]; } } - ( id ) setRecipient: ( id ) _recipient { [ liberación automática del destinatario ]; destinatario = [ retención del destinatario ]; devolver uno mismo ; } - ( id ) destinatario { devolver destinatario ; } @end
#importar <objc/Objeto.h>// Un objeto Destinatario simple. @interface Destinatario : Objeto - ( id ) hola ; @end
#import "Destinatario.h" Destinatario de @implementation- ( id ) hola { printf ( "El destinatario dice hola! \n " ); devuelve yo mismo ; } @fin
#import "Reenviador.h" #import "Destinatario.h"int main ( void ) { Reenviador * reenviador = [ Reenviador nuevo ]; Destinatario * destinatario = [ Destinatario nuevo ]; [ forwarder setRecipient : receiver ]; // Establece el destinatario. /* * ¡Observa que el reenviador no responde a un mensaje de saludo! Se * reenviará. Todos los métodos no reconocidos se * reenviarán al destinatario * (si el destinatario responde a ellos, como está escrito en el reenviador) */ [ forwarder hello ]; [ liberación del destinatario ]; [ liberación del reenvío ]; devuelve 0 ; }
Cuando se compila utilizando gcc , el compilador informa:
$ gcc -x objective-c -Wno-import Forwarder.m Recipient.m main.m -lobjc main.m: En la función `main': main.m:12: advertencia: `Forwarder' no responde a `hello' $
El compilador informa sobre el punto mencionado anteriormente, que Forwarder
no responde a los mensajes de saludo. En esta circunstancia, es seguro ignorar la advertencia ya que se implementó el reenvío. Al ejecutar el programa se produce este resultado:
$ ./a.out ¡ El destinatario dice hola!
Durante el diseño de Objective-C, una de las principales preocupaciones era la capacidad de mantenimiento de grandes bases de código. La experiencia del mundo de la programación estructurada había demostrado que una de las principales formas de mejorar el código era dividirlo en partes más pequeñas. Objective-C tomó prestado y amplió el concepto de categorías de las implementaciones de Smalltalk para ayudar con este proceso. [25]
Además, los métodos dentro de una categoría se agregan a una clase en tiempo de ejecución . Por lo tanto, las categorías permiten al programador agregar métodos a una clase existente (una clase abierta ) sin la necesidad de volver a compilar esa clase o incluso tener acceso a su código fuente. Por ejemplo, si un sistema no contiene un corrector ortográfico en su implementación de String, se podría agregar sin modificar el código fuente de String.
Los métodos dentro de las categorías se vuelven indistinguibles de los métodos de una clase cuando se ejecuta el programa. Una categoría tiene acceso total a todas las variables de instancia dentro de la clase, incluidas las variables privadas.
Si una categoría declara un método con la misma firma de método que un método existente en una clase, se adopta el método de la categoría. Por lo tanto, las categorías no solo pueden agregar métodos a una clase, sino también reemplazar métodos existentes. Esta característica se puede utilizar para corregir errores en otras clases reescribiendo sus métodos o para provocar un cambio global en el comportamiento de una clase dentro de un programa. Si dos categorías tienen métodos con el mismo nombre pero firmas de método diferentes, no está definido qué método de la categoría se adopta.
Otros lenguajes han intentado añadir esta característica de diversas formas. TOM llevó el sistema Objective-C un paso más allá y permitió también la adición de variables. Otros lenguajes han utilizado soluciones basadas en prototipos , siendo la más notable Self .
Los lenguajes C# y Visual Basic (.NET) implementan funciones superficialmente similares en forma de métodos de extensión , pero estos carecen de acceso a las variables privadas de la clase. [26] Ruby y varios otros lenguajes de programación dinámica se refieren a la técnica como " parcheo de mono ".
Logtalk implementa un concepto de categorías (como entidades de primera clase) que subsume la función de categorías de Objective-C (las categorías de Logtalk también se pueden usar como unidades de composición de grano fino al definir, por ejemplo, nuevas clases o prototipos; en particular, una categoría de Logtalk puede ser importada virtualmente por cualquier número de clases y prototipos).
Este ejemplo crea una Integer
clase definiendo primero una clase básica con solo métodos de acceso implementados y agregando dos categorías, Arithmetic
y Display
, que extienden la clase básica. Si bien las categorías pueden acceder a los miembros de datos privados de la clase base, a menudo es una buena práctica acceder a estos miembros de datos privados a través de los métodos de acceso, lo que ayuda a mantener las categorías más independientes de la clase base. Implementar dichos métodos de acceso es un uso típico de las categorías. Otro es usar categorías para agregar métodos a la clase base. Sin embargo, no se considera una buena práctica usar categorías para anular subclases, también conocido como parcheo de mono . Los protocolos informales se implementan como una categoría en la NSObject
clase base. Por convención, los archivos que contienen categorías que extienden clases base tomarán el nombre BaseClass+ExtensionClass.h .
#importar <objc/Objeto.h>@interface Entero : Objeto { int entero ; } - ( int ) entero ; - ( id ) entero: ( int ) _integer ; @end
#importar "Entero.h"@implementation Entero - ( int ) entero { devolver entero ; } - ( id ) entero: ( int ) _integer { entero = _integer ; devuelve self ; } @end
#importar "Entero.h"@interface Entero (Aritmética) - ( id ) suma: ( Entero * ) sumando ; - ( id ) sub: ( Entero * ) sustraendo ; @end
#import "Entero+Aritmética.h"@implementation Entero (Aritmética) - ( id ) add: ( Entero * ) addend { return [ entero propio : [ entero propio ] + [ addend entero ]]; } - ( id ) sub: ( entero * ) sustraendo { return [ entero propio : [ entero propio ] - [ entero sustraendo ]]; } @end
#importar "Entero.h"@interface Entero (Mostrar) - ( id ) showstars ; - ( id ) showint ; @end
# importar "Integer+Display.h"@implementation Entero (Mostrar) - ( id ) muestra estrellas { int i , x = [ entero propio ]; para ( i = 0 ; i < x ; i ++ ) { printf ( "*" ); } printf ( " \n " ); devuelve yo mismo ; } - ( id ) showint { printf ( "%d \n " , [ entero propio ]); devuelve yo mismo ; } @end
#importar "Entero.h"#import "Entero+Aritmética.h"#import "Entero+Pantalla.h"int principal ( vacío ) { Entero * num1 = [ Entero nuevo ], * num2 = [ Entero nuevo ]; entero x ; printf ( "Ingrese un entero: " ); scanf ( "%d" , & x ); [ num1 entero : x ]; [ num1 estrellas del espectáculo ]; printf ( "Ingrese un entero: " ); scanf ( "%d" , & x ); [ num2 entero : x ]; [ num2 estrellas del espectáculo ]; [ num1 suma : num2 ]; [ num1 muestraint ]; devuelve 0 ; }
La compilación se realiza, por ejemplo, mediante:
$ gcc -x objective-c main.m Entero.m Entero+Aritmética.m Entero+Visualización.m -lobjc
Se puede experimentar omitiendo #import "Integer+Arithmetic.h"
(línea 2) y (línea 21) y omitiendo en la compilación. El programa seguirá ejecutándose. Esto significa que es posible combinar y mezclar categorías agregadas si es necesario; si una categoría no necesita tener alguna habilidad, simplemente no se puede compilar.[num1 add:num2]
Integer+Arithmetic.m
Objective-C permite que una clase reemplace por completo a otra clase dentro de un programa. Se dice que la clase que reemplaza "se hace pasar por" la clase de destino.
La presentación de clases se declaró obsoleta en Mac OS X v10.5 y no está disponible en el entorno de ejecución de 64 bits . Se puede lograr una función similar mediante el uso de la combinación de métodos en categorías, que intercambia la implementación de un método con la de otro que tenga la misma firma.
En las versiones que aún admiten la función de posado, todos los mensajes enviados a la clase de destino son recibidos por la clase de posado. Existen varias restricciones:
La presentación, al igual que las categorías, permite la ampliación global de las clases existentes. La presentación permite dos características que no están presentes en las categorías:
Por ejemplo,
@interface AplicaciónNS personalizada : AplicaciónNS @fin@implementation CustomNSApplication - ( void ) setMainMenu: ( NSMenu * ) menu { // hacer algo con el menú } @end class_poseAs ([ clase CustomNSApplication ], [ clase NSApplication ]);
Esto intercepta cada invocación de setMainMenu a NSApplication.
En el lenguaje C, la #include
directiva de precompilación siempre hace que el contenido de un archivo se inserte en el código fuente en ese punto. Objective-C tiene la #import
directiva, equivalente excepto que cada archivo se incluye solo una vez por unidad de compilación, lo que evita la necesidad de protecciones de inclusión .
// ARCHIVO: hola.m #import <Foundation/Foundation.h> int main ( int argc , const char * argv []) { /* mi primer programa en Objective-C */ NSLog ( @"¡Hola, mundo! \n " ); return 0 ; }
$ # Compilar línea de comandos para gcc y compilador MinGW: $ gcc \ $ ( gnustep-config --objc-flags ) \ -o hello \ hello.m \ -L /GNUstep/System/Library/Libraries \ -lobjc \ -lgnustep-base $ ./hola
Las características de Objective-C a menudo permiten soluciones flexibles y, a menudo, fáciles a los problemas de programación.
Objective-C++ es una variante del lenguaje aceptada por el front-end de GNU Compiler Collection y Clang , que puede compilar archivos fuente que utilizan una combinación de sintaxis de C++ y Objective-C. Objective-C++ agrega a C++ las extensiones que Objective-C agrega a C. Como no se hace nada para unificar la semántica detrás de las diversas características del lenguaje, se aplican ciertas restricciones:
Classname *
) se pueden usar como parámetros de plantilla C++.En la Conferencia Mundial de Desarrolladores de 2006 , Apple anunció el lanzamiento de "Objective-C 2.0", una revisión del lenguaje Objective-C que incluye "recolección de basura moderna, mejoras de sintaxis, [29] mejoras de rendimiento en tiempo de ejecución, [30] y compatibilidad con 64 bits". Mac OS X v10.5 , lanzado en octubre de 2007, incluía un compilador Objective-C 2.0. GCC 4.6 admite muchas características nuevas de Objective-C, como propiedades declaradas y sintetizadas, sintaxis de puntos, enumeración rápida, métodos de protocolo opcionales, atributos de método/protocolo/clase, extensiones de clase y una nueva API de tiempo de ejecución GNU Objective-C. [31]
El nombre Objective-C 2.0 representa una ruptura en el sistema de control de versiones del lenguaje, ya que la última versión de Objective-C para NeXT fue "objc4". [32] Este nombre de proyecto se mantuvo en la última versión del código fuente del entorno de ejecución de Objective-C heredado en Mac OS X Leopard (10.5). [33]
Objective-C 2.0 proporcionó un recolector de basura generacional y conservador opcional . Cuando se ejecuta en modo compatible con versiones anteriores , el entorno de ejecución convirtió las operaciones de conteo de referencias como "retain" y "release" en no-ops . Todos los objetos estaban sujetos a la recolección de basura cuando se habilitaba la recolección de basura. Los punteros C regulares se podían calificar con "__strong" para activar también las intercepciones del compilador de barrera de escritura subyacentes y, por lo tanto, participar en la recolección de basura. [34] También se proporcionó un subsistema de cero débil de modo que los punteros marcados como "__weak" se establecen en cero cuando se recolecta el objeto (o, más simplemente, la memoria GC). El recolector de basura no existe en la implementación iOS de Objective-C 2.0. [35] La recolección de basura en Objective-C se ejecuta en un subproceso en segundo plano de baja prioridad y puede detenerse en eventos de usuario, con la intención de mantener la experiencia del usuario responsiva. [36]
La recolección de basura quedó obsoleta en Mac OS X v10.8 en favor del conteo automático de referencias (ARC). [37] Objective-C en iOS 7 que se ejecuta en ARM64 usa 19 bits de una palabra de 64 bits para almacenar el conteo de referencias, como una forma de punteros etiquetados . [38] [39]
Objective-C 2.0 introduce una nueva sintaxis para declarar variables de instancia como propiedades , con atributos opcionales para configurar la generación de métodos de acceso. Las propiedades son, en cierto sentido, variables de instancia públicas; es decir, declarar una variable de instancia como una propiedad proporciona a las clases externas acceso (posiblemente limitado, p. ej., solo lectura) a esa propiedad. Una propiedad puede declararse como "solo lectura" y puede proporcionarse con semántica de almacenamiento como assign
, copy
o retain
. De forma predeterminada, las propiedades se consideran atomic
, lo que da como resultado un bloqueo que evita que varios subprocesos accedan a ellas al mismo tiempo. Una propiedad puede declararse como nonatomic
, lo que elimina este bloqueo.
@interface Persona : NSObject { @public NSString * nombre ; @private int edad ; } @property ( copiar ) NSString * nombre ; @property ( solo lectura ) int edad ; - ( id ) initWithAge: ( int ) edad ; @end
Las propiedades se implementan mediante la @synthesize
palabra clave, que genera métodos getter (y setter, si no son de solo lectura) según la declaración de la propiedad. Alternativamente, los métodos getter y setter deben implementarse explícitamente, o la @dynamic
palabra clave puede usarse para indicar que los métodos de acceso se proporcionarán por otros medios. Cuando se compila utilizando clang 3.1 o superior, todas las propiedades que no se declaran explícitamente con @dynamic
, están marcadas readonly
o tienen métodos getter y setter implementados por el usuario completos se convertirán automáticamente en @synthesize
'd implícitamente.
@implementation Persona @synthesize nombre ; - ( id ) initWithAge: ( int ) initAge { self = [ super init ]; if ( self ) { // NOTA: asignación directa de variable de instancia, no establecedor de propiedades age = initAge ; } return self ; } - ( int ) edad { devolver edad ; } @end
Se puede acceder a las propiedades utilizando la sintaxis tradicional de paso de mensajes, notación de puntos o, en la codificación clave-valor, por nombre a través de los métodos "valueForKey:"/"setValue:forKey:".
Persona * aPerson = [[ Person alloc ] initWithAge : 53 ]; aPerson . name = @"Steve" ; // NOTA: notación de punto, utiliza un establecedor sintetizado, // equivalente a [aPerson setName: @"Steve"]; NSLog ( @"Acceso por mensaje (%@), notación de punto(%@), nombre de propiedad(% @) y " "acceso directo a variable de instancia(% @) " , [ aPerson name ], aPerson . name , [ aPerson valueForKey : @"name" ], aPerson -> name );
Para utilizar la notación de puntos para invocar descriptores de acceso de propiedades dentro de un método de instancia, self
se debe utilizar la palabra clave:
- ( void ) introduceMyselfWithProperties: ( BOOL ) useGetter { NSLog ( @"Hola, mi nombre es %@." , ( useGetter ? self . name : name )); // NOTA: acceso a getter vs. ivar }
Las propiedades de una clase o protocolo pueden introspeccionarse dinámicamente .
int i ; int propertyCount = 0 ; objc_property_t * propertyList = class_copyPropertyList ([ clase aPerson ], & propertyCount ); para ( i = 0 ; i < propertyCount ; i ++ ) { objc_property_t * thisProperty = propertyList + i ; const char * propertyName = property_getName ( * thisProperty ); NSLog ( @"La persona tiene una propiedad: '%s'" , propertyName ); }
Objective-C 2.0 proporciona variables de instancia no frágiles cuando el entorno de ejecución lo permite (es decir, al compilar código para macOS de 64 bits y todos los iOS). En el entorno de ejecución moderno, se agrega una capa adicional de indirección al acceso a las variables de instancia, lo que permite que el enlazador dinámico ajuste el diseño de la instancia en el entorno de ejecución. Esta característica permite dos mejoras importantes en el código Objective-C:
En lugar de utilizar un objeto NSEnumerator o índices para iterar a través de una colección, Objective-C 2.0 ofrece la sintaxis de enumeración rápida. En Objective-C 2.0, los siguientes bucles son funcionalmente equivalentes, pero tienen diferentes características de rendimiento.
// Usando NSEnumerator NSEnumerator * enumerador = [ thePeople objectEnumerator ]; Persona * p ; mientras (( p = [ enumerador nextObject ]) != nil ) { NSLog ( @"%@ tiene %i años." , [ p nombre ], [ p edad ]); }
// Uso de índices para ( int i = 0 ; i < [ thePeople count ]; i ++ ) { Persona * p = [ thePeople objectAtIndex : i ]; NSLog ( @"%@ tiene %i años." , [ p nombre ], [ p edad ]); }
// Usando enumeración rápida para ( Persona * p en laPersonas ) { NSLog ( @"%@ tiene %i años." , [ p nombre ], [ p edad ]); }
La enumeración rápida genera un código más eficiente que la enumeración estándar porque las llamadas a métodos para enumerar objetos se reemplazan por aritmética de punteros utilizando el protocolo NSFastEnumeration. [40]
Una extensión de clase tiene la misma sintaxis que una declaración de categoría sin nombre de categoría, y los métodos y propiedades declarados en ella se agregan directamente a la clase principal. Se utiliza principalmente como una alternativa a una categoría para agregar métodos a una clase sin anunciarlos en los encabezados públicos, con la ventaja de que para las extensiones de clase el compilador verifica que todos los métodos declarados de forma privada estén realmente implementados. [41]
Todas las aplicaciones Objective-C desarrolladas para macOS que utilizan las mejoras mencionadas anteriormente para Objective-C 2.0 son incompatibles con todos los sistemas operativos anteriores a la versión 10.5 (Leopard). Dado que la enumeración rápida no genera exactamente los mismos archivos binarios que la enumeración estándar, su uso provocará que una aplicación se bloquee en Mac OS X versión 10.4 o anterior.
Blocks es una extensión no estándar para Objective-C (y C y C++ ) que utiliza una sintaxis especial para crear cierres . Blocks solo es compatible con Mac OS X 10.6 "Snow Leopard" o posterior, iOS 4 o posterior y GNUstep con libobjc2 1.7 y compilación con clang 3.1 o posterior. [42]
#include <stdio.h> #include <Block.h> tipodef int ( ^ IntBlock )(); IntBlock MakeCounter ( int inicio , int incremento ) { __block int i = inicio ; devolver Bloque_copia ( ^ { int ret = i ; i += incremento ; devolver ret ; }); }int main ( void ) { IntBlock mycounter = MakeCounter ( 5 , 2 ); printf ( "Primera llamada: %d \n " , mycounter ()); printf ( "Segunda llamada: %d \n " , mycounter ()); printf ( "Tercera llamada: %d \n " , mycounter ()); /* porque fue copiado, también debe ser liberado */ Block_release ( mycounter ); devuelve 0 ; } /* Salida: Primera llamada: 5 Segunda llamada: 7 Tercera llamada: 9 */
Apple ha añadido algunas características adicionales a Objective 2.0 con el tiempo. Las adiciones sólo se aplican al "compilador Apple LLVM ", es decir, al frontend clang del lenguaje. Es confuso que el sistema de versiones utilizado por Apple difiera del sistema original de LLVM; consulte Xcode § Versiones de la cadena de herramientas para obtener una traducción a los números de versión de código abierto de LLVM. [43]
El conteo automático de referencias (ARC) es una característica de tiempo de compilación que elimina la necesidad de que los programadores administren manualmente los recuentos de retención utilizando retain
y release
. [44] A diferencia de la recolección de basura , que ocurre en tiempo de ejecución, ARC elimina la sobrecarga de un proceso separado que administra los recuentos de retención. ARC y la administración manual de memoria no son mutuamente excluyentes; los programadores pueden continuar usando código que no sea ARC en proyectos habilitados para ARC deshabilitando ARC para archivos de código individuales. Xcode también puede intentar actualizar automáticamente un proyecto a ARC.
ARC se introdujo en LLVM 3.0, lo que se traduce como Xcode 4.2 (2011) o compilador Apple LLVM 3.0. [45]
Los entornos de ejecución de NeXT y Apple Obj-C incluyen desde hace tiempo una forma abreviada de crear nuevas cadenas, utilizando la sintaxis literal @"a new string"
, o drop to CoreFoundation constants kCFBooleanTrue
y kCFBooleanFalse
for NSNumber
con valores booleanos. El uso de este formato evita que el programador tenga que utilizar initWithString
métodos más largos o similares al realizar determinadas operaciones.
Al utilizar el compilador Apple LLVM 4.0 (Xcode 4.4) o posterior, también se pueden crear matrices, diccionarios y números ( clases NSArray
, NSDictionary
, ) utilizando sintaxis literal en lugar de métodos. [46] (El compilador Apple LLVM 4.0 se traduce a LLVM y Clang 3.1 de código abierto). [47]NSNumber
Ejemplo sin literales:
NSArray * myArray = [ NSArray arrayWithObjects : objeto1 , objeto2 , objeto3 , nulo ]; NSDictionary * myDictionary1 = [ NSDictionary dictionaryWithObject : algúnObjeto paraClave : @"clave" ]; NSDictionary * myDictionary2 = [ NSDictionary dictionaryWithObjectsAndKeys : objeto1 , clave1 , objeto2 , clave2 , nulo ]; NSNumber * myNumber = [ NSNumber numberWithInt : myInt ]; NSNumber * mySumNumber = [ NSNumber numberWithInt : ( 2 + 3 )]; NSNumber * myBoolNumber = [ NSNumber numberWithBool : YES ];
Ejemplo con literales:
NSArray * miMatriz = @[ objeto1 , objeto2 , objeto3 ] ; NSDictionary * miDictionary1 = @{ @"clave" : algúnObjeto } ; NSDictionary * miDictionary2 = @{ clave1 : objeto1 , clave2 : objeto2 } ; NSNumber * miNúmero = @( miEntero ) ; NSNumber * miNúmeroSuma = @( 2 + 3 ) ; NSNumber * miNúmeroBool = @YES ; NSNumber * miNúmeroEntero = @8 ;
Sin embargo, a diferencia de los literales de cadena , que se compilan en constantes en el ejecutable, estos literales se compilan en código equivalente a las llamadas de método anteriores. En particular, en la gestión de memoria con recuento de referencias manual, estos objetos se liberan automáticamente, lo que requiere un cuidado adicional cuando, por ejemplo, se utilizan con variables estáticas de función u otros tipos de variables globales.
Al utilizar el compilador Apple LLVM 4.0 o posterior, es posible manipular matrices y diccionarios ( NSArray
y clases) mediante subíndices. [46] Los subíndices se pueden utilizar para recuperar valores de índices (matriz) o claves (diccionario), y con objetos mutables, también se pueden utilizar para establecer objetos en índices o claves. En el código, los subíndices se representan mediante corchetes . [48]NSDictionary
[ ]
Ejemplo sin subíndices:
id objeto1 = [ algunaMatriz objetoEnÍndice : 0 ]; id objeto2 = [ algúnDiccionario objetoParaClave : @"clave" ]; [ algunaMatrizMutable reemplazarObjetoEnÍndice : 0 conObjeto : objeto3 ]; [ algúnDiccionarioMutable establecerObjeto : objeto4 paraClave : @"clave" ];
Ejemplo con subíndices:
id objeto1 = algunaMatriz [ 0 ]; id objeto2 = algúnDiccionario [ @"clave" ]; algunaMatrizMutable [ 0 ] = objeto3 ; algúnDiccionarioMutable [ @"clave" ] = objeto4 ;
Después de la compra de NeXT por parte de Apple, se intentó hacer que el lenguaje fuera más aceptable para los programadores más familiarizados con Java que con Smalltalk. Uno de estos intentos fue introducir lo que en aquel momento se denominó "sintaxis moderna" para Objective-C [49] (en contraste con la sintaxis "clásica" actual). No hubo ningún cambio en el comportamiento, se trataba simplemente de una sintaxis alternativa. En lugar de escribir una invocación de método como:
objeto = [[ MyClass alloc ] init ]; [ objeto primeraEtiqueta : parámetro1 segundaEtiqueta : parámetro2 ];
En cambio, fue escrito como
objeto = ( MiClase . alloc ). init ; objeto . etiquetas ( parámetro1 , parámetro2 );
De manera similar, las declaraciones pasaron de la forma
- ( void ) primeraEtiqueta : ( int ) parámetro1 segundaEtiqueta : ( int ) parámetro2 ;
a
- ( void ) etiquetas ( int parámetro1 , int parámetro2 );
Esta sintaxis "moderna" ya no es compatible con los dialectos actuales del lenguaje Objective-C.
El proyecto mulle-objc es otra reimplementación de Objective-C. Admite compiladores GCC o Clang / LLVM como backend. Se diferencia de otros entornos de ejecución en términos de sintaxis, semántica y compatibilidad con ABI. Es compatible con Linux, FreeBSD y Windows.
Además de la implementación GCC / NeXT / Apple , que agregó varias extensiones a la implementación original de Stepstone , también existe otra implementación gratuita y de código abierto de Objective-C llamada Portable Object Compiler. [50] El conjunto de extensiones implementadas por Portable Object Compiler difiere de la implementación GCC/NeXT/Apple; en particular, incluye bloques similares a Smalltalk para Objective-C, mientras que carece de protocolos y categorías, dos características ampliamente utilizadas en OpenStep y sus derivados y parientes. En general, POC representa una etapa anterior a NeXT en la evolución del lenguaje, aproximadamente conforme al libro de Brad Cox de 1991.
También incluye una biblioteca de tiempo de ejecución llamada ObjectPak, que se basa en la biblioteca ICPak101 original de Cox (que a su vez deriva de la biblioteca de clases Smalltalk-80) y es bastante radicalmente diferente de OpenStep FoundationKit.
El sistema PC GEOS utilizaba un lenguaje de programación conocido como GEOS Objective-C o goc ; [51] a pesar de la similitud del nombre, los dos lenguajes son similares solo en el concepto general y en el uso de palabras clave prefijadas con el signo @.
El paquete de compiladores Clang , parte del proyecto LLVM , implementa Objective-C y otros lenguajes. Después de que GCC 4.3 (2008) pasara a la GPLv3, Apple lo abandonó en favor de clang, un compilador que tiene más poder legal para modificar. Como resultado, muchas de las características del lenguaje Objective-C moderno solo son compatibles con Clang.
El esquema de control de versiones de Apple para su "compilador LLVM" basado en clang difiere del control de versiones de código abierto de LLVM. Consulte Xcode § Versiones de la cadena de herramientas para obtener una traducción [43]
El proyecto GNU ha estado interesado durante mucho tiempo en una plataforma a la que portar programas NeXT y Obj-C. El registro de cambios del directorio libobjc en GCC sugiere que existía antes de 1998 (GCC 2.95), y su README apunta además a una reescritura en 1993 (GCC 2.4). [52]
El código fuente del frontend de NeXT fue publicado porque se creó como parte de GCC, y se publicó la Licencia Pública General de GNU que obliga a quienes realizan trabajos derivados a hacerlo. [ ¿Cuándo? ] Apple continuó con esta tradición al publicar su bifurcación de GCC hasta la versión 4.2.1, después de lo cual abandonaron el compilador. Los mantenedores de GCC aceptaron los cambios, pero no invirtieron mucho en brindar soporte para características más nuevas como el lenguaje Objective-C 2.0. [32] : ¿Qué compilador?
Los desarrolladores de GNUstep, interesados en el nuevo lenguaje, bifurcaron la libobjc de GCC en un proyecto independiente de GCC llamado libobjc2 en 2009. También organizaron que el entorno de ejecución se usara con Clang para aprovechar la nueva sintaxis del lenguaje. [32] : ¿Qué compilador ? GCC se movió lentamente al mismo tiempo, pero en GCC 4.6.0 (2011) también han pasado a Objective-C 2.0 en su libobjc. [31] [53] La documentación de GNUstep sugiere que la implementación de GCC aún carece de soporte para bloques, variables no frágiles y el nuevo ARC. [32] : ¿Qué entorno de ejecución?
Microsoft bifurcó libobjc2 en una parte de WinObjC , el puente iOS para la Plataforma Universal de Windows , en 2015. Combinado con su propia implementación de Cocoa Touch y API subyacentes, el proyecto permite la reutilización del código de aplicación iOS dentro de aplicaciones UWP. [54]
En Windows, las herramientas de desarrollo Objective-C se proporcionan para su descarga en el sitio web de GNUStep. El sistema de desarrollo de GNUStep consta de los siguientes paquetes: GNUstep MSYS System, GNUstep Core, GNUstep Devel, GNUstep Cairo, ProjectCenter IDE (como Xcode, pero no tan complejo), Gorm (Interface Builder como el generador NIB de Xcode). Estos instaladores binarios no se han actualizado desde 2016, [55] por lo que podría ser una mejor idea simplemente instalar compilando bajo Cygwin o MSYS2 .
Objective-C hoy en día se utiliza a menudo en tándem con una biblioteca fija de objetos estándar (a menudo conocida como un "kit" o "marco"), como Cocoa , GNUstep u ObjFW. Estas bibliotecas a menudo vienen con el sistema operativo: las bibliotecas GNUstep a menudo vienen con distribuciones basadas en Linux y Cocoa viene con macOS. El programador no está obligado a heredar funciones de la clase base existente (NSObject - OFObject). Objective-C permite la declaración de nuevas clases raíz que no heredan ninguna función existente. Originalmente, los entornos de programación basados en Objective-C normalmente ofrecían una clase Object como clase base de la que heredaban casi todas las demás clases. Con la introducción de OpenStep, NeXT creó una nueva clase base llamada NSObject, que ofrecía características adicionales sobre Object (un énfasis en el uso de referencias a objetos y el conteo de referencias en lugar de punteros sin formato, por ejemplo). Casi todas las clases en Cocoa heredan de NSObject.
El cambio de nombre no sólo sirvió para diferenciar el nuevo comportamiento predeterminado de las clases dentro de la API de OpenStep, sino que permitió que el código que utilizaba Object (la clase base original utilizada en NeXTSTEP y, más o menos, otras bibliotecas de clases Objective-C) coexistiera en el mismo entorno de ejecución con el código que utilizaba NSObject (con algunas limitaciones). La introducción del prefijo de dos letras también se convirtió en una forma simplista de espacios de nombres, de los que Objective-C carece. El uso de un prefijo para crear un identificador de empaquetado informal se convirtió en un estándar de codificación informal en la comunidad Objective-C y continúa hasta el día de hoy.
Más recientemente, han empezado a aparecer gestores de paquetes, como CocoaPods , que pretende ser tanto un gestor de paquetes como un repositorio de paquetes. Una gran cantidad de código Objective-C de código abierto que se escribió en los últimos años ahora se puede instalar utilizando CocoaPods.
Las implementaciones de Objective-C utilizan un sistema de ejecución delgado escrito en C [ cita requerida ] , que agrega poco al tamaño de la aplicación. En contraste, la mayoría de los sistemas orientados a objetos en el momento en que se creó usaban grandes tiempos de ejecución de máquinas virtuales . Los programas escritos en Objective-C tienden a no ser mucho más grandes que el tamaño de su código y el de las bibliotecas (que generalmente no necesitan incluirse en la distribución del software), en contraste con los sistemas Smalltalk donde se usaba una gran cantidad de memoria solo para abrir una ventana. Las aplicaciones Objective-C tienden a ser más grandes que las aplicaciones similares de C o C++ porque el tipado dinámico de Objective-C no permite que los métodos se eliminen o se incorporen en línea. Dado que el programador tiene tanta libertad para delegar, reenviar llamadas, crear selectores sobre la marcha y pasarlos al sistema de ejecución, el compilador Objective-C no puede asumir que es seguro eliminar los métodos no utilizados o incorporar llamadas en línea.
De la misma manera, el lenguaje puede implementarse sobre compiladores C existentes (en GCC , primero como preprocesador, luego como módulo) en lugar de como un nuevo compilador. Esto permite a Objective-C aprovechar la enorme colección existente de código C, bibliotecas, herramientas, etc. Las bibliotecas C existentes pueden envolverse en envoltorios Objective-C para proporcionar una interfaz de estilo OO. En este aspecto, es similar a la biblioteca GObject y al lenguaje Vala , que se utilizan ampliamente en el desarrollo de aplicaciones GTK .
Todos estos cambios prácticos redujeron la barrera de entrada , probablemente el mayor problema para la aceptación generalizada de Smalltalk en la década de 1980.
Una crítica común es que Objective-C no tiene soporte de lenguaje para espacios de nombres . En cambio, los programadores se ven obligados a agregar prefijos a sus nombres de clase, que tradicionalmente son más cortos que los nombres de espacios de nombres y, por lo tanto, más propensos a colisiones. A partir de 2007, todas las clases y funciones de macOS en el entorno de programación Cocoa tienen el prefijo "NS" (por ejemplo, NSObject, NSButton) para identificarlas como pertenecientes al núcleo de macOS o iOS; el "NS" deriva de los nombres de las clases tal como se definieron durante el desarrollo de NeXTSTEP .
Dado que Objective-C es un superconjunto estricto de C, no trata los tipos primitivos de C como objetos de primera clase .
A diferencia de C++ , Objective-C no admite la sobrecarga de operadores . Además, a diferencia de C++, Objective-C permite que un objeto herede directamente de una sola clase (lo que prohíbe la herencia múltiple ). Sin embargo, en la mayoría de los casos, las categorías y los protocolos se pueden utilizar como formas alternativas de lograr los mismos resultados.
Debido a que Objective-C utiliza tipado dinámico en tiempo de ejecución y a que todas las llamadas a métodos son llamadas a funciones (o, en algunos casos, llamadas al sistema), muchas optimizaciones de rendimiento comunes no se pueden aplicar a los métodos Objective-C (por ejemplo: inserción en línea, propagación constante, optimizaciones interprocedimentales y reemplazo escalar de agregados). Esto limita el rendimiento de las abstracciones Objective-C en relación con abstracciones similares en lenguajes como C++, donde dichas optimizaciones son posibles.
Las primeras versiones de Objective-C no admitían la recolección de basura . En su momento, esta decisión fue motivo de debate y muchas personas consideraron que los largos "tiempos muertos" (cuando Smalltalk realizaba la recolección) hacían que todo el sistema quedara inutilizable. Algunas implementaciones de terceros han añadido esta función (la más notable es GNUstep con Boehm ), y Apple la ha implementado a partir de Mac OS X v10.5 . [56] Sin embargo, en versiones más recientes de macOS e iOS, la recolección de basura ha quedado obsoleta en favor del conteo automático de referencias (ARC), introducido en 2011.
Con ARC, el compilador inserta llamadas de retención y liberación automáticamente en el código Objective-C basándose en el análisis de código estático . La automatización libera al programador de tener que escribir código de gestión de memoria. ARC también agrega referencias débiles al lenguaje Objective-C. [57]
El diseño y la implementación de C++ y Objective-C representan enfoques fundamentalmente diferentes para extender C.
Además del estilo de programación procedimental de C, C++ admite directamente ciertas formas de programación orientada a objetos , programación genérica y metaprogramación . C++ también viene con una gran biblioteca estándar que incluye varias clases contenedoras . De manera similar, Objective-C agrega programación orientada a objetos , tipado dinámico y reflexión a C. Objective-C no proporciona una biblioteca estándar per se , pero en la mayoría de los lugares donde se usa Objective-C, se usa con una biblioteca similar a OpenStep como OPENSTEP , Cocoa o GNUstep , que proporciona funciones similares a la biblioteca estándar de C++.
Una diferencia notable es que Objective-C proporciona soporte en tiempo de ejecución para funciones de programación reflexiva , mientras que C++ agrega solo una pequeña cantidad de soporte en tiempo de ejecución a C. En Objective-C, se puede consultar a un objeto sobre sus propias propiedades, por ejemplo, si responderá a un determinado mensaje. En C++, esto no es posible sin el uso de bibliotecas externas.
El uso de la reflexión forma parte de la distinción más amplia entre las características dinámicas (en tiempo de ejecución) y las características estáticas (en tiempo de compilación) de un lenguaje. Aunque Objective-C y C++ emplean una combinación de ambas características, Objective-C favorece las decisiones en tiempo de ejecución, mientras que C++ favorece las decisiones en tiempo de compilación. La tensión entre la programación dinámica y la estática implica muchas de las disyuntivas clásicas de la programación: las características dinámicas añaden flexibilidad, las estáticas añaden velocidad y comprobación de tipos.
La programación genérica y la metaprogramación se pueden implementar en ambos lenguajes mediante polimorfismo en tiempo de ejecución ( despacho dinámico ). En C++, esto toma la forma de funciones virtuales e identificación de tipo en tiempo de ejecución , mientras que Objective-C ofrece tipado dinámico y reflexión. Tanto Objective-C como C++ admiten polimorfismo en tiempo de compilación ( funciones genéricas ), y Objective-C agregó esta característica en 2015.
El lenguaje Swift es el producto del esfuerzo incansable de un equipo de expertos en lenguajes, gurús de la documentación, ninjas de la optimización de compiladores y un grupo interno de pruebas internas increíblemente importante que brindó comentarios para ayudar a refinar y probar ideas. Por supuesto, también se benefició enormemente de las experiencias ganadas con esfuerzo por muchos otros lenguajes en el campo, tomando ideas de Objective-C, Rust,
Haskell
,
Ruby
,
Python
,
C#
, CLU y muchos otros para enumerarlos.
El problema surgió por primera vez cuando NeXT propuso distribuir un GCC modificado en dos partes y dejar que el usuario las enlazara. Jobs me preguntó si esto era legal. En ese momento me pareció que lo era, siguiendo un razonamiento como el que estás usando; pero como el resultado era muy indeseable para el software libre, dije que tendría que preguntarle al abogado. Lo que dijo el abogado me sorprendió; dijo que los jueces considerarían tales esquemas como "subterfugios" y serían muy duros con ellos. Dijo que un juez preguntaría si es "realmente" un programa, en lugar de cómo está etiquetado. Entonces volví a Jobs y le dije que creíamos que su plan no estaba permitido por la GPL. El resultado directo de esto es que ahora tenemos una interfaz Objective-C. Querían distribuir el analizador Objective C como un paquete propietario separado para vincularlo con el back end de GCC, pero como no estaba de acuerdo en que esto estuviera permitido, lo hicieron gratuito.
Objective-C es un superconjunto estricto de ANSI C.
Objective-C es un superconjunto estricto de C orientado a objetos.
Objective-C es un superconjunto de C
El lenguaje de programación Objective-C es un superconjunto del lenguaje de programación C.
La extensión .m originalmente significaba "mensajes" cuando se introdujo Objective-C por primera vez, haciendo referencia a una característica central de Objective-C.
Por ahora, es sensato versionar este documento según las versiones de su única implementación (y su proyecto anfitrión), clang. "LLVM XY" se refiere a una versión de código abierto de clang del proyecto LLVM. "Apple XY" se refiere a una versión proporcionada por Apple del compilador Apple LLVM.
El entorno de ejecución se ha reescrito por completo en gcc 2.4. El entorno de ejecución anterior tenía varios errores graves y estaba bastante incompleto.