Objectiu-C

De la Viquipèdia, l'enciclopèdia lliure.
Saltar a la navegació Saltar a la cerca
Objectiu-C
llenguatge de programació
Autor Brad Cox i Tom Love ( Stepstone Corp. )
Data d’origen 1983
Última versió 2.0
Ús llenguatge d’ús general
Paradigmes programació orientada a objectes
Escrivint feble , dinàmic
Extensions comunes h, m, mm i C
Influenciat per C , Smalltalk
Va influir C ++ , Swift , Java
Implementació de referència
Sistema operatiu Multiplataforma
Lloc web developer.apple.com/library/mac/navigation/

Objective-C, també es coneix sovint com a objectiu C o ObjC o ObjC, és un objecte- orientat reflexiu llenguatge de programació desenvolupat per Brad Cox a mitjans anys 1980 al StepStone Corporation.

Com el seu nom indica, Objectiu C és una extensió d'objecte del llenguatge C. Manté una compatibilitat completa amb C (a diferència del que passa, per exemple, en el cas de C ++ ). Entre altres coses, també a causa d’aquesta compatibilitat, Objective C no té una tipografia forta (una característica que presenten, entre d’altres, tant C ++ com Java ).

Les extensions d’objectes amb els quals l’objectiu C enriqueix el model semàntic de C s’inspiren en el llenguatge Smalltalk , en particular en la gestió de missatges . Les característiques del sistema d'execució situen l'objectiu C entre els llenguatges d'objectes dinàmics. S'admeten tots els elements clàssics de la programació orientada a objectes; però tampoc no falten conceptes innovadors en aquest front, com ara el mecanisme de categories i eines vinculades a la reflexió .

La seva difusió es relaciona principalment amb el framework NeXT OpenStep i el seu successor Cocoa , present al sistema operatiu macOS d' Apple . NeXT és responsable del suport de l’objectiu C al compilador GNU gcc .

Història

Els orígens

A principis dels anys vuitanta , la pràctica habitual de l’enginyeria de programari es basava en la programació estructurada . Aquest mode es va desenvolupar per poder dividir programes grans en parts més petites, principalment per facilitar el desenvolupament de programari i el treball de manteniment. No obstant això, a mesura que creixia la mida dels problemes a resoldre, la programació estructurada va ser cada vegada menys útil, ja que va conduir a l’escriptura d’un nombre creixent de procediments, a un codi spaghetti i a una escassa reutilització del codi font .

Aleshores es va suggerir que la programació orientada a objectes podria ser una solució potencial al problema. De fet, Smalltalk ja havia abordat molts d’aquests problemes d’enginyeria, tot i que tenia l’inconvenient de necessitar una màquina virtual que interpretés un objecte en memòria anomenat imatge que contenia totes les eines necessàries. La imatge de Smalltalk era molt gran, solia utilitzar una gran quantitat de memòria per al moment i funcionava molt lentament a causa de la manca de suport de maquinari específic per a màquines virtuals.

L'objectiu C va ser creat principalment per Brad Cox i Tom Love a principis dels anys vuitanta a Stepstone . Tots dos havien estat introduïts a Smalltalk durant el seu temps al Centre de Tecnologia de Programació d’ ITT Corporation el 1981 . Cox havia començat a interessar-se pels problemes de reutilització del programari i va trobar que un llenguatge semblant a Smalltalk seria extremadament valuós per crear entorns de desenvolupament potents per als dissenyadors d’ITT. Cox va començar a modificar el compilador C per afegir algunes de les funcions de Smalltalk. Aviat va obtenir una implementació de treball d’una extensió orientada a objectes del llenguatge C que va anomenar OOPC (Object-Oriented Programming in C). Mentrestant, Love va ser contractat per Schlumberger Research el 1982 i va tenir l'oportunitat d'adquirir la primera còpia comercial de Smalltalk-80, que més tard va influir en el desenvolupament de la seva creació.

Per demostrar que el llenguatge era un progrés real, Cox va demostrar que eren necessàries poques adaptacions pràctiques a les eines existents per fer components de programari intercanviables. En concret, era necessari donar suport als objectes d’una manera flexible amb un conjunt de biblioteques de programari que fossin utilitzables i que permetessin recopilar el codi font (i els recursos necessaris pel codi) en un únic format multiplataforma .

Cox i Love van acabar formant una nova empresa , Productivity Products International (PPI), per comercialitzar el seu producte que emparellava un compilador Objective C amb una potent classe de biblioteques.

El 1986 Cox va publicar la seva descripció de Objective C en la seva forma original al llibre Object-Oriented Programming, An Evolutionary Approach . Tot i que va tenir cura d’assenyalar que la qüestió de la reutilització del programari no es podia esgotar amb el llenguatge de programació , l’objectiu C sovint es trobava comparat, característica per característica, amb altres llenguatges.

Pròxim

El 1988 , NeXT , l’empresa fundada per Steve Jobs després d’ Apple , va llicenciar Objective C de Stepstone (aleshores propietari de la marca ) i va construir el seu propi compilador Objective C i biblioteques en què basava la interfície d’usuari de NeXTSTEP . Tot i que les estacions de treball NeXTSTEP no van tenir un fort impacte al mercat, la indústria va apreciar les seves eines. Això va portar NeXT a abandonar la producció de maquinari i centrar-se en eines de programari, venent NeXTSTEP (i OpenStep ) com a plataforma de programació.

Més tard, el projecte GNU va començar a treballar en el clon lliure que anomenava GNUstep , basat en l'estàndard OpenStep. Dennis Glatting va escriure el primer temps d' execució de gnu-objc el 1992 i Richard Stallman aviat el va seguir amb un segon. El temps d'execució de GNU Objective C, que es fa servir des del 1993 , va ser desenvolupat per Kresten Krab Thorup mentre era estudiant universitari a Dinamarca .

poma

Després d’adquirir NeXT el 1996 , Apple va utilitzar OpenStep com a base del seu nou sistema operatiu macOS . Això li va permetre incloure l’objectiu C de NeXT i el seu sistema de desenvolupament de Project Builder (més tard rebatejat com Xcode ). La majoria de les API actuals d'Apple ( API Cocoa ) es basen en els objectes de la interfície OpenStep i constitueixen l'entorn de desenvolupament basat en Objective C més significatiu que s'utilitza actualment.

Desenvolupaments

Avui

Avui l’objectiu C s’utilitza sovint junt amb biblioteques fixes d’objectes estàndard (sovint anomenats "kits" o " frameworks ") com Cocoa o GNUstep . Aquestes biblioteques se subministren sovint junt amb el sistema operatiu : les biblioteques GNUstep formen part de la col·lecció de programari GNU i de vegades estan presents en algunes distribucions Gnu / Linux i Cocoa de MacOS . El programador no està obligat a heretar la funcionalitat de la classe base existent (NSObject). L'objectiu C permet declarar noves classes base que no hereten cap de les funcions preexistents. Els entorns de programació basats en Objective C originalment oferien la classe Object amb algunes funcionalitats bàsiques, però amb la introducció d' OpenStep , NeXT va crear una nova classe base anomenada NSObject que oferia funcions addicionals més enllà de les de Object. Gairebé totes les classes de cacau hereten de NSObject.

El canvi de nom no només va servir per diferenciar els nous comportaments de classe en el nou marc, sinó que també va permetre que el codi que encara utilitzava la classe Object coexisteixi (encara que amb algunes limitacions) en el mateix sistema de temps d'execució . Al mateix temps, la introducció del prefix de dues lletres s’ha convertit en una mena de substitut per la manca de nom d’espai (o d’espais de noms, si ho preferiu) a l’objectiu C. L'ús d'un prefix per crear un identificador de paquet informal s'ha convertit en una pràctica estàndard a la comunitat de programació Objective C.

Objectiu C 2.0

A la Worldwide Developers Conference del 2006 , Apple va anunciar el llançament d '"Objective C 2.0" que inclou recollida d' escombraries , millores de sintaxi [1] , millores del rendiment en temps d'execució [2] i 64 bits [3] . Encara no se sap quan s'admetran aquestes evolucions en temps d'execució de GNU, tot i que ja són compatibles amb Mac OS X Leopard [4] .

Recollida d'escombraries

L’objectiu C 2.0 permet la recollida d’escombraries , però de manera dependent del sistema operatiu. No obstant això, és possible utilitzar-lo cap enrere, de manera que el codi font escrit per a versions anteriors continua funcionant.

Propietat

Tot i que anteriorment eren variables d'instància que requerien mètodes explícits de lectura i escriptura (anomenats getters i setters), Objective C 2.0 introdueix les propietats (propietat) amb la sintaxi següent:

 @interface Person : NSObject {
 }
 @property (només de lectura) nom NSString * ;
 @property (només de lectura) int age;
 - ( id ) initWithName :( NSString ) name eta :( int ) age ;
 @final

Un cop introduïdes a la interfície, es pot accedir a les propietats mitjançant la notació descrita a l'exemple:

 NSString * nom = aPersona . nom ;

El compilador tradueix aquesta notació en mètodes accessoris. La instrucció anterior equival a:

 NSString * nom = [ un nom de persona ];

Enumeració ràpida

En lloc d’utilitzar un objecte enumerador per recórrer una col·lecció d’objectes, Objective C 2.0 ofereix una sintaxi de bucle dedicada; prenent l'exemple anterior:

 per a ( Person * persona in laGente )
  NSLog ( @ "% @ té% i anys." , Persona . Nom , persona . Edat );

Compilador d'objectes portàtils

A més de les implementacions de GCC / NeXT / Apple , que han afegit diverses extensions a la Stepstone original, hi ha una altra implementació de codi obert de l’objectiu C que afegeix un conjunt d’extensions una mica diferent: el compilador d’objectes portàtils [1] implementa entre més també alguns blocs de codi a l'estil Smalltalk.

Sintaxi

L’objectiu C és una capa prima sobre el llenguatge C ; Per tant, C és un subconjunt estret de l’objectiu C. Per tant, és possible compilar qualsevol programa escrit en C amb un compilador de l’objectiu C. La major part de la sintaxi (clàusules, expressions , declaracions i funcions del preprocessador ) es deriva de la de C , mentre que la sintaxi relacionada amb les característiques orientades a objectes es va crear per obtenir l’ intercanvi de comunicació de missatges similar a la de Smalltalk .

Missatges

La sintaxi afegida respecte a C està pensada per donar suport a la programació orientada a objectes. El model de programació de l’objectiu C es basa en l’intercanvi de missatges entre objectes tal com passa a Smalltalk . Aquest model és diferent del de Simula , que s’utilitza en nombrosos idiomes com, entre d’altres, C ++ . Aquesta distinció és semànticament important i consisteix principalment en el fet que a l’objectiu C no es crida un mètode , sinó que s’envia un missatge .

Es diu que un objecte anomenat ogg la classe del qual implementa el mètode doSomething respon al missatge doSomething . L’enviament del missatge doSomething a l’objecte ogg s’expressa mitjançant:

 [ ogg doSomething ];

mentre que l'acció equivalent en C ++ i Java s'expressaria per:

 ogg . fer alguna cosa ();

D'aquesta manera és possible enviar missatges a un objecte encara que l'objecte no pugui respondre. Això difereix de estàticament mecanografiats llenguatges com C ++ i Java en el qual totes les trucades han de ser a mètodes predefinits.

Interfícies i implementacions

L'objectiu C requereix que la interfície i la implementació d'una classe es declaren en diferents blocs de codi. Per convenció, la interfície es posa en un fitxer amb el sufix ".h", mentre que la implementació en un fitxer amb el sufix ".m".

Interfície

La interfície d'una classe es defineix normalment en un fitxer ".h". La convenció que s’utilitza consisteix a anomenar el fitxer en funció del nom de la classe, a l’exemple "NomeDellaClasse.h".

 // definició de la interfície: "ClassName.h"

 #import "NameOfSuperclass.h"

 @interface ClassName : SuperclassName
 {
    // variables d’instància
    int variable entera ;
    variable float Float ;
    ...
 }
 // mètodes de classe
 + methodOfClass1
 + methodOfClass2
 + ...

 // mètodes d’instància
 - methodOf Instance1
 - methodOf Instance2
 - ...

 @final

El signe menys (-) indica els mètodes de la instància, mentre que el signe més (+) és la classe (anàloga a les funcions estàtiques de C ++). Tingueu en compte la diferència de significat amb les convencions dels diagrames UML on els dos signes representen els mètodes públics i privats respectivament.

Implementació

La interfície només declara els prototips dels mètodes i no els propis mètodes que s’insereixen a la implementació. La implementació s'escriu normalment en un fitxer amb l'extensió ".m". La convenció que s’utilitza consisteix a anomenar el fitxer en funció del nom de la classe, a l’exemple "NomeDellaClasse.m"

 // definició de la implementació: "ClassName.m"

 #import "ClassName.h"

 @implementationNom de classe
 + methodOfClass1
 {
    // implementació
    ...
 }
  + methodOfClass2
 {
    // implementació
    ...
 }
 ...
 - methodOf Instance1
 {
    // implementació
    ...
 }
 - methodOf Instance2
 {
    // implementació
    ...
 }
 ...

 @final

Els mètodes s'escriuen de manera diferent de les funcions d' estil C. Per exemple, una funció, tant en C com en l'objectiu C, segueix la següent forma general:

 int make_the_square_root ( int i )
 {
    tornar arrel_quadrada ( i );
 }

que tindrà com a prototip:

 int make_the_square_root ( int );

La implementació com a mètode es convertirà en:

 - ( int ) make_the_square_root :( int ) i
 {
    retorn [lliure square_root: i];
 }

Un enfocament més canònic per escriure el mètode seria citar el primer argument al nom del selector:

 - ( int ) faiLaRadiceQuadrataDiInt : ( int ) i
 {
    return [ auto rootSquareDiInt : i ];
 }

Aquesta sintaxi pot semblar complicada, però us permet assignar noms a paràmetres , per exemple:

 - ( int ) changeColorWithRed :( int ) r verd :( int ) g blau :( int ) b

es pot invocar així:

 [ myColor changeColorWithRed : 5 verd : 2 blau : 6 ];

Les representacions internes d’aquests mètodes poden variar segons les diferents implementacions de l’objectiu C.

Si MyColor , a l’exemple anterior, era de la classe Color , internament el mètode d’instància -changeColorWithRed: green: blue: es podria etiquetar _i_Color_changeColorWithRed_green_blue , on he seguit el nom de la classe, es refereix al fet que és un mètode d’instància i un dos punts (:) se substitueix per un subratllat (_). Com que l'ordre dels paràmetres forma part del nom del mètode, no es pot canviar.

En qualsevol cas, els noms interns de les funcions rarament s’utilitzen directament i, en general, els missatges enviats també es converteixen en funcions definides a les biblioteques en temps d’execució i no accedeixen directament als noms interns. Això també es deu al fet que en el moment de la compilació no sempre se sap a quin mètode es cridarà realment, perquè la classe de destinatari (l'objecte al qual s'envia el missatge) pot ser desconeguda fins a l'execució.

Protocols

NeXT va ampliar l’objectiu C per introduir el concepte d’ herència d’ especificació múltiple , però no d’implementació, mitjançant l’ús de protocols. Aquest és un patró que es pot obtenir tant mitjançant una forma d’herència múltiple d’una classe abstracta (com a C ++), com (com més habitualment a Java o C # ) mitjançant l’ús d’una interfície (fins i tot a C ++ hi ha interfícies, també si no hi ha cap paraula clau explícita per declarar-les). L’objectiu C fa ús dels dos protocols, anomenats protocols informals i protocols imposats pel compilador, anomenats protocols formals .

Un protocol informal és una llista de mètodes que una classe pot implementar. S’especifica a la documentació, ja que no està explícitament present en l’idioma. Els protocols informals sovint inclouen mètodes opcionals, on la implementació del mètode pot canviar el comportament de la classe. Per exemple, una classe amb un camp de text pot tenir un "delegat" que hauria d'implementar un protocol informal amb un mètode opcional d' autocompleció . El camp de text esbrina si el delegat implementa o no el mètode (mitjançant el mecanisme de reflexió ) i, en cas afirmatiu, el crida per donar suport a la realització automàtica.

Un protocol formal és similar a una interfície Java o C #. Consisteix en una llista de mètodes que cada classe pot pretendre implementar. El compilador notificarà un error si la classe no implementa tots els mètodes dels protocols que declara. El concepte de protocols Objective C difereix del de les interfícies Java i C # en què una classe pot implementar un protocol sense declarar-lo explícitament. La diferència no es pot distingir fora del codi. Els protocols formals no poden proporcionar cap implementació, simplement asseguren a les persones que truquen que les classes que s’ajusten al protocol proporcionaran implementacions. A les biblioteques NeXT / Apple , el sistema d’objectes distribuït fa servir sovint protocols per representar les capacitats d’un objecte que s’executa en un sistema remot.

La sintaxi

 @protocol bloqueig
 - bloqueig ( buit ) ;
 - ( nul ) desbloqueig ;
 @final

indica que hi ha una idea abstracta de bloqueig que es pot utilitzar; quan es declara en una definició de classe

 @interface ClassName: NomeSuperClasse <Bloqueig>
 @final

indica que les instàncies ClassName proporcionaran una implementació per als dos mètodes d’instància com considerin oportú. Aquesta especificació abstracta és particularment útil per descriure el comportament desitjat, per exemple, dels connectors sense posar cap limitació al que ha de ser la jerarquia d’implementació.

Escriptura dinàmica

L’objectiu C (com Smalltalk) pot utilitzar la tipografia dinàmica ; és a dir, permet augmentar la flexibilitat, enviar a un objecte un missatge no definit a la seva interfície. A l’objectiu C un objecte pot "capturar" aquest missatge i el pot enviar a un altre objecte (que pot respondre correctament o, al seu torn, enviar el missatge a un altre objecte, etc.). Aquest comportament s’anomena reenviament (en italià : reenviament ) o delegació de missatges (vegeu més avall ). Com a alternativa, podeu utilitzar un controlador d’errors en cas que no es pugui reenviar el missatge. Si l'objecte no reenvia el missatge, no gestiona l'error o no respon, es generarà un error en temps d'execució.

Opcionalment, es pot afegir informació de mecanografia estàtica a les variables. Aquesta informació es comprova en temps de compilació . A les instruccions següents, es proporciona informació cada vegada més específica. Les sentències són equivalents durant l'execució, però la informació permet al compilador advertir el programador si els arguments aprovats no coincideixen amb els tipus especificats. A la primera declaració, l’objecte s’ha d’ajustar al protocol aProtocol i, a la segona, ha de ser membre de la classe NSNumber .

 - setMyValue: ( id < aProtocol > ) foo ;
- setMyValue: ( NSNumber * ) foo ;

L’escriptura dinàmica pot ser una característica molt potent. Si implementeu classes de contenidors utilitzant llenguatges de tipus estàtic, com ara Java (anterior a la versió 1.5), el programador es veu obligat a escriure classes de contenidors per a objectes genèrics i després utilitzar la conversió de tipus per adaptar-los a objectes específics; aquesta conversió, però, contradiu la disciplina semàntica de la tipificació estàtica.

Reenviament

Com que l’objectiu C permet enviar un missatge a un objecte que pot no respondre-hi, l’objecte pot gestionar aquest missatge d’altres maneres. Un d’aquests podria ser el seu reenviament (en anglès : forwarding ) a un altre objecte capaç de respondre. El reenviament es pot utilitzar per implementar simplement alguns patrons de disseny, com ara el patró Observer o el patró Proxy .

El sistema d’execució d’ Objective C especifica un parell de mètodes de la classe Object

  • mètodes de reenviament:
 - ( retval_t ) endavant: ( SEL ) sel : ( arglist_t ) args ; // amb GCC
- ( id ) endavant: ( SEL ) sel : ( marg_list ) args ; // amb sistemes NeXT / Apple
  • mètodes d'acció:
 - ( retval_t ) performv: ( SEL ) sel : ( arglist_t ) args ; // amb GCC
- ( id ) performv: ( SEL ) sel : ( marg_list ) args ; // amb sistemes NeXT / Apple

i si un objecte vol implementar el reenviament només ha de " anul·lar " els mètodes de reenviament per definir el seu comportament. No cal performv:: mètodes performv:: action.

Exemple

Aquí teniu un exemple de programa que il·lustra els fonaments del reenviament.

Forwarder.h
 #import <objc / Object.h>
 
 @interface Forwarder : objecte
 {
    destinatari d' identificació ; // L'objecte al qual volem reenviar el missatge
 }
 
 // Mètodes accessoris
 - ( id ) destinatari ;
 - ( void ) setRecipient :( id ) _recipient ; 
 
 @final
Forwarder.m
 #import "Forwarder.h"
 
 @implementation Forwarder
 
 - endavant : ( SEL ) sel : ( marg_list ) args
 {
    / *
* Comproveu si el destinatari respon realment al missatge.
* Això pot ser desitjable o no, per exemple, si es tracta d'un destinatari
* no respon al missatge, pot reenviar-lo ell mateix.
* /
    if ([el destinatari respon a : sel ]) 
       tornar [ destinatari performv : sel : args ];
    en cas contrari
       return [ auto error : "El destinatari no respon" ];
 }
 
 - ( id ) setRecipient : ( id ) _recipient
 {
    destinatari = _recipient ;
    tornar a si mateix ;
 }
 
 - ( id ) destinatari
 {
    destinatari de retorn ;
 }
 @final
Destinatari.h
 #import <objc / Object.h>
 
 // Un simple objecte destinatari.
 @interface Destinatari : objecte
 - ( id ) hola ;
 @final
Destinatari.m
 #import "Destinatari.h"
 
 @implementation Destinatari
 
 - ( id ) hola
 {
    printf ( "El destinatari us saluda! \ n " );
 
    tornar a si mateix ;
 }
 
 @final
principal.m
 #import "Forwarder.h"
 #import "Destinatari.h"
 
 int
 principal ( buit )
 {
    Forwarder * forwarder = [ Forwarder new ];
    Destinatari * destinatari = [ Destinatari nou ];
 
    [ reenviador setRecipient : destinatari ]; // Estableix el destinatari
    / *
* Tingueu en compte que el "reenviador" no respon al missatge.
* Es reenviarà. Tots els mètodes no reconeguts seran
* reenviada al destinatari (si el gestiona,
* tal com s'esmenta a "Reenviador").
* /
    [ reenviador hola ]; 
 
    retorn 0 ;
 }

Nota

Si compiléssim l’exemple, el compilador informaria

 $ gcc -x objectiu-c -Wno-import Forwarder.m Destinatari.m main.m -lobjc
main.m: En la funció "main":
main.m: 12: advertència: "El reenviador" no respon a "hola"
$

El compilador informa de l'explicat anteriorment, que el Forwarder no respon al missatge. En alguns casos, aquest indicador pot ajudar a trobar errors, però en aquest cas es pot ignorar ja que s'ha implementat el reenviament. Per executar el programa n'hi haurà prou:

 $ ./a.out
El destinatari diu hola!

Categories

L'experiència en el món de la programació estructurada havia demostrat que una de les maneres de millorar l'estructura del codi font és dividir-lo en parts més petites. Per millorar aquest procés, l’objectiu C va introduir el concepte de categoria .

Les categories us permeten afegir mètodes a una classe per separat. El programador pot posar grups de mètodes relacionats en una categoria per fer-los més llegibles. Per exemple, podeu crear una categoria corrector ortogràfic "en" una cadena de tipus de objecte de recollir tots els mètodes de correcció ortogràfica en un sol lloc.

A més, els mètodes situats en una categoria s'afegeixen a la classe en temps d'execució . D'aquesta manera, les categories permeten al programador afegir mètodes a una classe existent sense necessitat de recompilació i sense necessitat de tenir el codi font de la mateixa. A l'exemple, si el vostre sistema no proporciona suport per a la correcció ortogràfica a la implementació de la classe String, podeu afegir-lo sense canviar-ne l'origen.

Els mètodes situats en categories són pràcticament part de la classe quan s’executa el programa. Una categoria també accedeix a totes les variables d’instància de la classe, fins i tot a les privades.

Les categories proporcionen una solució als problemes relacionats amb la "fragilitat de les classes base" pel que fa als mètodes.

Si declareu un mètode en una categoria amb la mateixa signatura que un mètode que ja existeix en una classe, s’adopta el mètode de la categoria. D’aquesta manera, les categories no només poden afegir mètodes a les classes, sinó que també poden substituir els mètodes existents. Aquesta característica es pot utilitzar per corregir errors en altres classes, simplement reescrivint els seus mètodes o per canviar el comportament d'una classe en un programa determinat. Si dues categories tenen mètodes amb la mateixa signatura, no es defineix quin mètode s'executarà realment.

Diferents idiomes han intentat afegir aquesta funció de diverses maneres. El llenguatge TOM ha portat el concepte més enllà, ja que també us permet afegir variables. Altres idiomes, com ara Self , han adoptat solucions orientades a prototips ,

Exemple

Aquest exemple construeix una classe Integer , primer definint una classe base amb només uns quants mètodes implementats i després afegint dues categories, Arithmetic i Display , que amplien la classe base. Tot i que aquestes categories poden accedir a les variables privades de classe base, és una bona pràctica accedir a aquestes variables mitjançant mètodes de servei que ajudin a mantenir les categories el més independents possible de les classes que amplien. Aquest és un cas típic d’utilitzar categories per afegir o modificar determinats mètodes de la classe base (tot i que no es considera una bona pràctica utilitzar categories per anul·lar les subclasses).

Enter.h
 #include <objc / Object.h>
 
@interface Integer : Object
{
   int integer ;
}

- ( int ) enter ;
- ( id ) enter: ( int ) _integer ;
@final
Enter.m
 #import "Integer.h"

@implementation Enter
- ( int ) enter
{
   retorn enter ;
}

- ( id ) enter: ( int ) _integer
{
   enter = _integer ;
   tornar a si mateix ;
}
@final
Aritmètica.h
 #import "Integer.h"

@interface Integer (Aritmètica)
- ( id ) add: ( Enter * ) addend ;
- ( id ) sub: ( enter * ) subtrahend ;
@final
Aritmètica.m
 #import "Arithmetic.h"

@implementation Integer (Aritmètica) 
- ( id ) add: ( Enter * ) addend
{
   retorn [acte número sencer: [acte nombre enter] + [sumant nombre enter]];
}

- ( id ) sub: ( enter * ) subtrahend
{
   retorn [acte número sencer: [acte nombre enter] - [subtrahend nombre enter]];
}
@final
Display.h
 #import "Integer.h"

@interface Integer (Display)
- ( id ) showstars ;
- ( id ) showint ;
@final
Visualització.m
 #import "Display.h"

@implementation Integer (Display) 
- ( id ) showstars
{
   int i, x = [interlocutòria número sencer];
   per a ( i = 0 ; i < x ; i ++ )
      printf ( "*" );
   printf ( " \ n " );

   tornar a si mateix ;
}

- ( id ) showint
{
   printf ( "% d \ n", [auto nombre enter]);

   tornar a si mateix ;
}
@final
main.m
 #import "Integer.h"
#import "Arithmetic.h"
#import "Display.h"

int main ( buit )
{
   Enter * num1 = [ Enter new ], * num2 = [ Enter new ];
   int x ;
   printf ( "Introduïu un nombre enter:" );
   scanf ( "% d" , & x );
   [ número1 enter : x ];
   [ num1 showstars ];
   printf ( "Introduïu un nombre enter:" );
   scanf ( "% d" , & x );
   [ número2 enter : x ];
   [ num2 showstars ];

   [ num1 afegir : num2 ];
   [ num1 showint ];
}

Nota

la compilació es fa de la següent manera:

 gcc -x objectiu-c main.m Enter.m Arithmetic.m Display.m -lobjc

Si può provare ad omettere le linee #import "Arithmetic.h" e [num1 add:num2] e ad omettere Arithmetic.m in compilazione. Il programma girerà lo stesso. Ciò significa che è possibile aggiungere o togliere categorie, dato che se non si ha bisogno di una certa funzionalità offerta da una categoria, basta semplicemente escluderla dalla compilazione.

Posing

Objective C permette ad una classe di sostituirne completamente un'altra; questo meccanismo è detto posing (dall' inglese pose as : fingersi per qualcun altro). La classe sostituita è chiamata classe target e la classe che sostituisce è chiamata classe posing . Tutti i messaggi inviati alla classe target vengono ricevuti in sua vece dalla classe posing. Esistono numerose restrizioni da rispettare per effettuare il posing:

  • Una classe può solo sostituirsi a una delle sue superclassi dirette o indirette.
  • La classe posing non deve definire nessuna nuova variabile d'istanza che sia assente dalla classe target (anche se può definire o sovrascrivere metodi).
  • Nessun messaggio deve essere inviato alla classe target prima del posing

Il posing, similmente alle categorie, consente un aumento globale delle classi esistenti e permette due possibilità assenti nelle categorie:

  • Una classe posing può chiamare metodi sovrascritti attraverso super , incorporando così l'implementazione della classe target.
  • Una classe posing può sovrascrivere i metodi definiti nelle categorie.

Ad esempio:

 @interface CustomNSApplication : NSApplication
 @end
 
 @implementation CustomNSApplication
 - ( void ) setMainMenu : ( NSMenu * ) menu
 {
     // fa qualcosa col menu
 }
 @end
 
 class_poseAs ([ CustomNSApplication class ], [ NSApplication class ]);

Questo intercetta ogni invocazione a setMainMenu di NSApplication.

Il posing è stato dichiarato deprecato con Mac OS X Leopard e non è disponibile nei run-time a 64 bit.

#import

In C, la direttiva del precompilatore #include consente di inserire un intero file prima dell'inizio effettivo della compilazione . Objective-C aggiunge a questa la direttiva #import , che oltre a svolgere lo stesso ruolo della precedente, evita di includere il file qualora sia già stato incluso in precedenza.

Ad esempio, il file A include i file X e Y, ma X e Y includono ciascuno il file Q, in questo caso Q verrebbe incluso due volte nel file risultante causando così delle definizioni duplicate e quindi un errore in compilazione. Se il file Q venisse incluso con la direttiva #import , solo la prima inclusione verrebbe effettivamente effettuata e tutte le successive verrebbero ignorate.

Alcuni compilatori, compreso GCC , supportano la clausola #import anche per il linguaggio C; il suo uso viene comunque scoraggiato sulla base del fatto che l'utilizzatore dei file da includere dovrebbe distinguere quali file includere solo una volta da quelli progettati per essere inclusi più volte. Questo onere dovrebbe in teoria essere a carico dell'implementatore del file da includere che può usare la direttiva #pragma once o usare la tradizionale tecnica :

 #ifndef H_PERSONA
#define H_PERSONA
// ... contenuto di header.h ...
#endif

In questo caso le direttive #include e #import diventano equivalenti.

Altre caratteristiche

Objective C ha incluso sin dal suo apparire un lista di caratteristiche che sono tuttora in via di acquisizione in altri linguaggi, oltre ad alcune che sono rimaste sue uniche prerogative. Ciò ha permesso di mettere in luce, partendo dalla realizzazione di Cox (ed in seguito da quella di NeXT ), che alcuni considerazioni superano i concetti più strettamente legati al linguaggio. Il sistema deve essere usabile e flessibile nel complesso per poter essere pienamente fruibile.

  • Delegare i metodi ad altri oggetti al run-time è banale. Basta semplicemente aggiungere una categoria comprendente le modifiche ad un metodo per implementare il forwarding al destinatario della delega.
  • La chiamata di procedura remota è banale. Basta semplicemente aggiungere una categoria con un metodo che " serializzi " l'invocazione e la inoltri.
  • Lo swizzling dei puntatori consente di modificare le classi al run-time. Tipicamente per scopi di debugging se un oggetto la cui memoria è stata rilasciata dovesse venire referenziato per errore.
  • Un oggetto può essere archiviato su uno stream (ad esempio un file ) e può essere riletto e recuperato su richiesta.

Objective C++

Objective C++ è un front-end del compilatore gcc in grado di compilare codice sorgente che usa una sintassi combinazione di C++ e Objective C. Objective C++ aggiunge a C++ le stesse estensioni che Objective C aggiunge a C. Dato che nulla è stato fatto per unificare le differenze semantiche tra i due linguaggi, sono state applicate alcune restrizioni:

  • una classe C++ non può derivare da una classe Objective C e viceversa
  • i namespace C++ non possono essere dichiarati all'interno di una dichiarazione Objective C
  • le classi Objective C non possono contenere variabili di istanza di classi C++ che non abbiano un costruttore di default o che abbiano uno o più metodi virtuali , ma si possono usare puntatori ad oggetti C++ come variabili di istanza senza restrizioni
  • la semantica "per valore" del C++ non può essere applicata agli oggetti Objective C, i quali rimangono accessibili solo mediante puntatori
  • non ci possono essere dichiarazioni Objective C in dichiarazioni di template C++ e viceversa. Comunque è possibile usare tipi Objective C (es. Nomeclasse *) come parametri di template C++
  • La gestione delle eccezioni Objective C è distinta da quella di C++

Analisi del linguaggio

L'implementazione dell'Objective C usa un semplice run-time system scritto in linguaggio C che aumenta di poco la dimensione delle applicazioni. Al contrario, la maggior parte dei sistemi object-oriented esistenti quando fu creato (e Java tuttora) usava una grossa macchina virtuale invasiva per l'intero sistema. I programmi scritti in Objective C tendono ad essere di poco più grandi delle dimensioni del loro codice oggetto e delle librerie usate (che generalmente non devono essere incluse nel codice distribuito), al contrario ad esempio dei sistemi Smalltalk dove grandi quantità di memoria sono necessarie semplicemente per aprire una finestra .

Il linguaggio può essere implementato con un compilatore C (in GCC , prima come un preprocessore ed in seguito come un modulo del compilatore) piuttosto che con un nuovo compilatore. Ciò consente all'Objective C di sfruttare l'enorme mole di codice, librerie e strumenti già esistenti in C che può essere adattata in Objective C per fornire un'interfaccia object-oriented. Tutte questi fattori riducono le barriere d'ingresso al nuovo linguaggio, fattore che costituì il problema principale di Smalltalk negli anni ottanta .

Le prime versioni di Objective C non supportavano la garbage collection . Al tempo questa scelta fu oggetto di discussioni e in molti (ai tempi di Smalltalk) la consideravano un lungo "tempo morto" in cui il sistema era reso non più utilizzabile. Anche se qualche implementazione di terze parti (principalmente GNUstep ) aveva già aggiunto questa caratteristica, è stata implementata da Apple una tecnica simile tramite ARC in Mac OS X Leopard , ma non è disponibile per applicazioni implementate per versioni precedenti del sistema operativo . [5]

Un'altra critica comunemente fatta all'Objective C è quella di non avere un supporto nativo per i namespace . I programmatori sono perciò costretti ad aggiungere prefissi in maniera più o meno arbitraria ai nomi delle classi che implementano, fatto che può causare collisioni. Dal 2007 tutte le classi e le funzioni di macOS in ambiente Cocoa hanno il prefisso "NS" (es. NSObject o NSButton ) per identificarle chiaramente; "NS" deriva dal nome delle classi definite durante lo sviluppo di NeXTSTEP .

Dato che Objective C è uno stretto superinsieme del C, non tratta i tipi primitivi del C come first-class object .

A differenza del C++, Objective C non supporta l' overloading degli operatori, consente l' ereditarietà solo diretta da una singola classe (vietando così l' ereditarietà multipla ). Dato che il linguaggio Java venne influenzato dall'Objective C, la decisione di usare l'ereditarietà singola venne portata anche in Java. In alternativa all'ereditarietà multipla possono essere usate le categorie ed i protocolli .

Differenze filosofiche tra Objective C e C++

Il progetto e l'implementazione del C++ e dell'Objective C rappresentano due diversi approcci all'estensione del C.

Oltre alla programmazione strutturata del C, C++ supporta direttamente la programmazione ad oggetti , la programmazione generica e la metaprogrammazione . C++ è inoltre corredato di una estesa libreria standard che include numerose classi container . L'Objective C, invece, aggiunge solo delle caratteristiche object-oriented al C; esso, nella sua versione più "pura" non offre lo stesso in termini di librerie standard, ma in molti contesti dove viene usato, viene corredato di una libreria sul modello di quella di OpenStep , di Cocoa o di GNUstep le quali forniscono funzionalità simili a quelle offerte dalla libreria standard di C++.

Un'altra notevole differenza consiste nel fatto che l'Objective C fornisce un maggior supporto run-time alla riflessione rispetto a C++. In Objective C si può interrogare un oggetto riguardo alle sue stesse proprietà, ad esempio se possa o meno rispondere ad un dato messaggio, mentre in C++ ciò è impossibile a meno di fare ricorso a librerie esterne. Comunque è possibile chiedere se due oggetti sono o meno dello stesso tipo (inclusi i tipi predefiniti) e se un oggetto è istanza di una data classe (o superclasse ).

L'uso della riflessione fa parte di una più ampia distinzione tra caratteristiche dinamiche ( run-time ) e statiche ( compile-time ) dei linguaggi. Sebbene sia Objective C che C++ implementino un misto di entrambe le caratteristiche, Objective C è decisamente più orientato verso le decisioni dinamiche, mentre C++ verso quelle effettuate al momento della compilazione.

Note

  1. ^ ( EN ) documento Apple , su lists.apple.com (archiviato dall' url originale il 18 giugno 2009) .
  2. ^ ( EN ) documento Apple , su lists.apple.com (archiviato dall' url originale il 24 novembre 2010) .
  3. ^ ( EN ) documento Apple , su developer.apple.com .
  4. ^ ( EN ) documento Apple , su apple.com . URL consultato il 3 maggio 2019 (archiviato dall' url originale il 15 dicembre 2008) .
  5. ^ ( EN ) Apple, Inc., Mac OS X Leopard – Xcode 3.0 , su apple.com , 22 agosto 2006. URL consultato il 22 agosto 2006 (archiviato dall' url originale il 24 ottobre 2007) .

Bibliografia

  • Object Oriented Programming: An Evolutionary Approach , Brad J. Cox - Addison Wesley (1991)

Collegamenti esterni

Controllo di autorità LCCN ( EN ) sh2008009199 · GND ( DE ) 4335874-3 · BNF ( FR ) cb14537421z (data)
Informatica Portale Informatica : accedi alle voci di Wikipedia che trattano di informatica