Obiectiv-C

De la Wikipedia, enciclopedia liberă.
Salt la navigare Salt la căutare
Obiectiv-C
limbaj de programare
Autor Brad Cox și Tom Love ( Stepstone Corp. )
Data de origine 1983
Ultima versiune 2.0
Utilizare limbaj de uz general
Paradigme programare orientată obiect
Tastare slab , dinamic
Extensii comune h, m, mm și C
Influențată de C , Smalltalk
A influențat C ++ , Swift , Java
Implementare referință
Sistem de operare Multiplatform
Site-ul web developer.apple.com/library/mac/navigation/

Obiectiv-C, de asemenea , adesea menționată ca obiectiv C sau ObjC sau ObjC, este un object- orientat reflexiv limbaj de programare dezvoltat de Brad Cox în mijlocul -1980s la StepStone Corporation.

După cum sugerează și numele, obiectivul C este o extensie obiect a limbajului C. Menține compatibilitatea completă cu C (spre deosebire de ceea ce se întâmplă, de exemplu, în cazul C ++ ). Printre altele, și datorită acestei compatibilități, Obiectivul C nu are o tastare puternică (o caracteristică pe care o prezintă, printre altele, atât C ++, cât și Java ).

Extensiile la obiecte cu care Obiectivul C îmbogățește modelul semantic al lui C sunt inspirate din limbajul Smalltalk , în special în gestionarea mesajelor . Caracteristicile sistemului de execuție plasează Obiectivul C printre limbajele obiect dinamice. Sunt suportate toate elementele clasice ale programării orientate pe obiecte; dar nu lipsesc nici conceptele inovatoare pe acest front, precum mecanismul categoriilor și instrumentelor legate de reflecție .

Difuzarea sa este legată în principal de cadrul NeXT OpenStep și de succesorul său Cocoa , prezent în sistemul de operare macOS Apple . NeXT este responsabil pentru suportul Objective C în compilatorul GNU gcc .

Istorie

Originile

La începutul anilor 1980 , practica obișnuită a ingineriei software se baza pe programarea structurată . Acest mod a fost dezvoltat pentru a putea împărți programele mari în părți mai mici, în principal pentru a facilita dezvoltarea software-ului și lucrările de întreținere. Cu toate acestea, pe măsură ce dimensiunea problemelor de rezolvat a crescut, programarea structurată a devenit din ce în ce mai puțin utilă, deoarece a condus la scrierea unui număr tot mai mare de proceduri, la un cod spaghetti și la o reutilizare redusă a codului sursă .

S-a sugerat apoi că programarea orientată pe obiecte ar putea fi o soluție potențială la problemă. De fapt, Smalltalk abordase deja multe dintre aceste probleme de inginerie, deși cu dezavantajul necesității unei mașini virtuale care să interpreteze un obiect în memorie numit o imagine care conține toate instrumentele necesare. Imaginea Smalltalk a fost foarte mare, a avut tendința de a utiliza o cantitate enormă de memorie pentru timpul respectiv și a funcționat foarte încet, din cauza lipsei suportului hardware specific pentru mașinile virtuale.

Obiectivul C a fost creat în primul rând de Brad Cox și Tom Love la începutul anilor 1980 la Stepstone . Ambii au fost introduși în Smalltalk în timpul petrecut la Centrul de tehnologie de programare ITT Corporation în 1981 . Cox începuse să se intereseze de problemele de reutilizare a software-ului și a constatat că un limbaj asemănător Smalltalk ar fi extrem de valoros pentru crearea unor medii de dezvoltare puternice pentru proiectanții ITT. Cox a început să modifice compilatorul C pentru a adăuga câteva dintre caracteristicile Smalltalk. Curând a obținut o implementare funcțională a unei extensii orientate obiect a limbajului C pe care a numit-o OOPC (Object-Oriented Programming in C). Între timp, Love a fost angajat de Schlumberger Research în 1982 și a avut ocazia să achiziționeze prima copie comercială a Smalltalk-80, care ulterior a influențat dezvoltarea creației lor.

Pentru a arăta că limbajul a fost un progres real, Cox a arătat că sunt necesare puține adaptări practice la instrumentele existente pentru a face componente software interschimbabile. Mai precis, a fost necesar să susținem obiectele într-un mod flexibil cu un set de biblioteci software care erau utilizabile și care permiteau colectarea codului sursă (și a oricăror resurse necesare codului) într-un singur format multiplataforma .

Cox și Love au format în cele din urmă o nouă companie , Productivity Products International (PPI), pentru a-și comercializa produsul care a asociat un compilator Objective C cu o clasă puternică de biblioteci.

În 1986, Cox și-a publicat descrierea Obiectivului C în forma sa originală în cartea Programare orientată pe obiecte, o abordare evolutivă . Deși a avut grijă să sublinieze că problema reutilizării software-ului nu poate fi epuizată de limbajul de programare , Obiectivul C s-a trezit adesea comparat, caracteristică cu caracteristică, cu alte limbaje.

Următorul

În 1988 , NeXT , compania fondată de Steve Jobs după Apple , a licențiat Objective C de la Stepstone (pe atunci proprietarul mărcii ) și și-a construit propriul compilator Objective C și biblioteci pe care a bazat interfața cu utilizatorul NeXTSTEP . Deși stațiile de lucru NeXTSTEP nu au reușit să aibă un impact puternic asupra pieței, instrumentele lor au fost apreciate pe scară largă de către industrie. Acest lucru a determinat NeXT să renunțe la producția de hardware și să se concentreze asupra instrumentelor software, vânzând NeXTSTEP (și OpenStep ) ca platformă de programare.

Mai târziu, proiectul GNU a început să lucreze la clona gratuită numită GNUstep , bazată pe standardul OpenStep. Dennis Glatting a scris primul run- gnu-objc în 1992 și Richard Stallman a urmat curând cu un al doilea. Executarea timpului GNU Objective C, care a fost utilizat din 1993 , a fost dezvoltată de Kresten Krab Thorup în timp ce era student universitar în Danemarca .

măr

După achiziționarea NeXT în 1996 , Apple a folosit OpenStep ca bază pentru noul său sistem de operare macOS . Acest lucru i-a permis să includă obiectivul C al NEXT și sistemul său de dezvoltare Project Builder (redenumit ulterior Xcode ). Majoritatea API-ului curent Apple ( API Cocoa ) se bazează pe obiectele interfeței OpenStep și constituie cel mai semnificativ mediu de dezvoltare bazat pe obiectivul C utilizat în prezent.

Dezvoltări

Azi

Astăzi, Obiectivul C este adesea utilizat în tandem cu bibliotecile fixe de obiecte standard (adesea denumite „kituri” sau „ cadre ”), cum ar fi Cocoa sau GNUstep . Aceste biblioteci sunt adesea furnizate împreună cu sistemul de operare : bibliotecile GNUstep fac parte din colecția de software GNU și sunt uneori prezente în unele distribuții Gnu / Linux și Cocoa din MacOS . Programatorul nu este obligat să moștenească funcționalitatea clasei de bază existente (NSObject). Obiectivul C permite declararea de noi clase de bază care nu moștenesc niciuna dintre funcționalitățile preexistente. Mediile de programare bazate pe Objective C ofereau inițial clasa Object cu unele funcționalități de bază, dar odată cu introducerea OpenStep , NeXT a creat o nouă clasă de bază numită NSObject care oferea caracteristici suplimentare dincolo de cele ale Object. Aproape toate clasele Cocoa moștenesc de la NSObject.

Redenumirea nu a servit doar pentru a diferenția comportamentele noii clase în noul cadru, ci a permis și codul care încă folosea clasa Object să coexiste (deși cu unele limitări) în același sistem de execuție . În același timp, introducerea prefixului din două litere a devenit un fel de surogat pentru lipsa spațiului (sau a spațiilor de nume, dacă preferați) în obiectivul C. Utilizarea unui prefix pentru a crea un identificator de pachet informal a devenit o practică standard în comunitatea de programare Objective C.

Obiectivul C 2.0

La Conferința mondială a dezvoltatorilor din 2006 , Apple a anunțat lansarea „Objective C 2.0” al cărei conținut este colectarea gunoiului , îmbunătățirea sintaxei [1] , îmbunătățirea performanței în timp de execuție [2] și suport pentru 64 de biți [3] . Nu se știe încă când aceste evoluții vor fi acceptate în runtime GNU, deși sunt deja acceptate în Mac OS X Leopard [4] .

Colectarea gunoiului

Obiectivul C 2.0 permite colectarea gunoiului , dar într-o manieră dependentă de sistemul de operare. Cu toate acestea, este posibil să-l utilizați înapoi, astfel încât codul sursă scris pentru versiunile anterioare să continue să funcționeze.

Proprietate

În timp ce anterior variabilele de instanță care necesită metode explicite de citire și scriere (numite getters și setere), Objective C 2.0 introduce proprietățile (proprietatea) cu următoarea sintaxă:

 @interface Persoana : NSObject {
 }
 @property (readonly) NSString * name;
 @property (readonly) int age;
 - ( id ) initWithName :( NSString ) name eta :( int ) age ;
 @Sfârșit

Odată introduse în interfață, proprietățile pot fi accesate folosind notația descrisă în exemplu:

 NSString * nume = aPerson . nume ;

Compilatorul traduce această notație în apeluri de metode accesorii. Instrucțiunea anterioară este echivalentă cu:

 NSString * nume = [ aPerson name ];

Enumerare rapidă

În loc să utilizeze un obiect enumerator pentru a itera printr-o colecție de obiecte, Objective C 2.0 oferă o sintaxă de buclă dedicată; luând exemplul anterior:

 pentru ( Person * persona in laGente )
  NSLog ( @ "% @ are% i ani." , Persoană . Nume , persoană . Vârstă );

Compilator de obiecte portabile

În plus față de implementările GCC / NeXT / Apple , care au adăugat mai multe extensii la cea originală Stepstone , există o altă implementare Open Source a obiectivului C care adaugă un set ușor diferit de extensii: Portable Object Compiler [1] implementează între mai multe de asemenea, câteva blocuri de cod în stilul Smalltalk.

Sintaxă

Obiectivul C este un strat subțire deasupra limbajului C ; Prin urmare, C este un subset restrâns al Obiectivului C. Rezultă că este posibil să se compileze orice program scris în C cu un compilator al Obiectivului C. Majoritatea sintaxei (clauze preprocesor , expresii , declarații și apeluri de funcții ) este derivată din cea a lui C , în timp ce sintaxa referitoare la caracteristicile orientate obiect a fost creată pentru a obține schimbul de comunicare de mesaje asemănător cu cel al Smalltalk .

Mesaje

Sintaxa adăugată cu privire la C este destinată să susțină programarea orientată pe obiecte. Modelul de programare al obiectivului C se bazează pe schimbul de mesaje între obiecte așa cum se întâmplă în Smalltalk . Acest model este diferit de cel al lui Simula , care este utilizat în numeroase limbi, cum ar fi, printre altele, C ++ . Această distincție este importantă din punct de vedere semantic și constă în principal în faptul că în Obiectivul C nu se apelează o metodă , ci se trimite un mesaj .

Se spune că un obiect numit ogg a cărui clasă implementează metoda doSomething răspunde la mesajul doSomething . Trimiterea mesajului doSomething către obiectul ogg se exprimă prin:

 [ ogg doSomething ];

în timp ce acțiunea echivalentă în C ++ și Java ar fi exprimată prin:

 ogg . face ceva ();

În acest fel, este posibil să trimiteți mesaje către un obiect chiar dacă obiectul nu poate răspunde. Acest lucru diferă de limbajele tipizate static, cum ar fi C ++ și Java, în care toate apelurile trebuie să fie către metode predefinite.

Interfețe și implementări

Obiectivul C necesită ca interfața și implementarea unei clase să fie declarate în diferite blocuri de cod. Prin convenție, interfața este pusă într-un fișier cu sufixul ".h", în timp ce implementarea într-un fișier cu sufixul ".m".

Interfață

Interfața unei clase este de obicei definită într-un fișier ".h". Convenția utilizată este de a denumi fișierul pe baza numelui clasei, în exemplul „NomeDellaClasse.h”.

 // definiția interfeței: "ClassName.h"

 #import "NameOfSuperclass.h"

 @interface ClassName : SuperclassName
 {
    // variabile de instanta
    int variabilă întreagă ;
    variabilă float Float ;
    ...
 }
 // metode de clasă
 + methodOfClass1
 + methodOfClass2
 + ...

 // metode de instanță
 - methodOf Instance1
 - methodOf Instance2
 - ...

 @Sfârșit

Semnul minus (-) denotă metodele de instanță, în timp ce semnul plus (+) clasa (analog funcțiilor statice ale C ++). Rețineți diferența de sens cu convențiile diagramelor UML în care cele două semne reprezintă metodele private și, respectiv, publice.

Implementare

Interfața declară doar prototipurile metodelor și nu metodele în sine care sunt inserate în implementare. Implementarea este de obicei scrisă într-un fișier cu extensia „.m”. Convenția utilizată este de a denumi fișierul pe baza numelui clasei, în exemplul „NomeDellaClasse.m”

 // definiție implementare: "ClassName.m"

 #import "ClassName.h"

 @implementation ClassName
 + methodOfClass1
 {
    // implementare
    ...
 }
  + methodOfClass2
 {
    // implementare
    ...
 }
 ...
 - methodOf Instance1
 {
    // implementare
    ...
 }
 - methodOf Instance2
 {
    // implementare
    ...
 }
 ...

 @Sfârșit

Metodele sunt scrise diferit de funcțiile în stil C. De exemplu, o funcție, atât în ​​C cât și în Obiectivul C, urmează următoarea formă generală:

 int make_the_square_root ( int i )
 {
    returnează rădăcină pătrată ( i );
 }

care va avea ca prototip:

 int make_the_square_root ( int );

Implementarea ca metodă va deveni:

 - ( int ) make_the_square_root :( int ) i
 {
    return [ self root_root : i ];
 }

O abordare mai canonică a scrierii metodei ar fi citarea primului argument în numele selectorului:

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

Această sintaxă poate părea complicată, dar vă permite să atribuiți nume parametrilor , de exemplu:

 - ( int ) changeColorWithRed :( int ) r verde :( int ) g albastru :( int ) b

poate fi invocat astfel:

 [ myColor changeColorWithRed : 5 verde : 2 albastru : 6 ];

Reprezentările interne ale acestor metode pot varia cu diferite implementări ale obiectivului C.

Dacă MyColor , în exemplul anterior, era din clasa Color , intern metoda instanței -changeColorWithRed: verde: albastru: ar putea fi etichetată _i_Color_changeColorWithRed_green_blue , unde am urmat de numele clasei, se referă la faptul că este o metodă de instanță și o colon (:) este înlocuit cu un subliniat (_). Deoarece ordinea parametrilor face parte din denumirea metodei, aceasta nu poate fi modificată.

În orice caz, numele interne ale funcțiilor sunt rareori folosite direct și, în general, mesajele trimise sunt, de asemenea, convertite în funcții definite în bibliotecile de execuție și nu accesează direct numele interne. Acest lucru se datorează, de asemenea, faptului că, la momentul compilării , nu se știe întotdeauna ce metodă va fi efectiv apelată, deoarece clasa destinatarului (obiectul către care este trimis mesajul) poate fi necunoscută până la rulare.

Protocoale

Obiectivul C a fost extins de NeXT pentru a introduce conceptul de specificație multiplă , dar nu implementarea, moștenirea prin utilizarea protocoalelor. Acesta este un model care poate fi obținut atât printr-o formă de moștenire multiplă dintr-o clasă abstractă (ca în C ++), cât și (ca mai frecvent în Java sau C # ) prin utilizarea unei interfețe (chiar și în C ++ există interfețe, de asemenea, dacă nu există un cuvânt cheie explicit care să le declare). Obiectivul C face uz de ambele protocoale, numite protocoale informale și protocoale impuse de compilator, numite protocoale formale .

Un protocol informal este o listă de metode care pot fi implementate de o clasă. Este specificat în documentație, deoarece nu este prezent în mod explicit în limbă. Protocoalele informale includ adesea metode opționale, în care implementarea metodei poate schimba comportamentul clasei. De exemplu, o clasă cu câmp text ar putea avea un „delegat” care ar trebui să implementeze un protocol informal cu o metodă opțională de completare automată . Câmpul text află dacă delegatul implementează sau nu metoda (prin mecanismul de reflecție ) și, dacă da, îl cheamă pentru a sprijini completarea automată.

Un protocol formal este similar cu o interfață Java sau C #. Acesta constă dintr-o listă de metode pe care fiecare clasă poate pretinde că le implementează. Compilatorul va raporta o eroare dacă clasa nu implementează fiecare metodă a protocoalelor pe care le declară. Conceptul de protocoale Objective C diferă de cel al interfețelor Java și C # prin aceea că o clasă poate implementa un protocol fără a-l declara în mod explicit. Diferența nu se distinge în afara codului. Protocoalele formale nu pot oferi nicio implementare, ci asigură pur și simplu apelanților că clasele care se conformează protocolului vor furniza implementări. În bibliotecile NeXT / Apple , protocoalele sunt utilizate frecvent de sistemul de obiecte distribuite pentru a reprezenta capacitățile unui obiect care rulează pe un sistem la distanță.

Sintaxa

 @protocol Blocare
 - blocare ( nulă ) ;
 - ( nul ) deblocare ;
 @Sfârșit

indică faptul că există o idee abstractă de blocare care poate fi utilizată; când este declarat într-o definiție de clasă

 @interface ClassName: NomeSuperClasse <Locking>
 @Sfârșit

indică faptul că instanțele ClassName vor furniza o implementare pentru cele două metode de instanță după cum consideră potrivit. Această specificație abstractă este deosebit de utilă pentru descrierea comportamentului dorit, de exemplu, pentru pluginuri, fără a pune nicio limitare asupra a ceea ce ar trebui să fie ierarhia de implementare.

Tastare dinamică

Obiectivul C (cum ar fi Smalltalk) poate utiliza tastarea dinamică ; adică face posibilă creșterea flexibilității, trimiterea unui obiect un mesaj nedefinit în interfața sa. În Obiectivul C un obiect poate „captura” acest mesaj și îl poate trimite unui alt obiect (care poate răspunde corect sau, la rândul său, poate trimite mesajul către alt obiect și așa mai departe). Acest comportament se numește redirecționare (în italiană : redirecționare ) sau delegare de mesaje (vezi mai jos ). Alternativ, puteți utiliza un gestionar de erori în cazul în care mesajul nu poate fi redirecționat. Dacă obiectul nu redirecționează mesajul, nu gestionează eroarea sau nu răspunde, va fi generată o eroare la runtime.

Informațiile de tastare statică pot fi adăugate opțional la variabile. Aceste informații sunt verificate în timpul compilării . În instrucțiunile următoare, sunt furnizate informații din ce în ce mai specifice. Instrucțiunile sunt echivalente în timpul execuției, dar informațiile permit compilatorului să avertizeze programatorul dacă argumentele transmise nu corespund tipurilor specificate. În prima declarație, obiectul trebuie să respecte protocolul aProtocol și, în a doua, trebuie să fie membru al clasei NSNumber .

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

Tastarea dinamică poate fi o caracteristică foarte puternică. Dacă implementați clase de containere utilizând limbaje tipizate static, cum ar fi Java (înainte de versiunea 1.5), programatorul este obligat să scrie clase de containere pentru obiecte generice și apoi să utilizeze conversia de tip pentru a le adapta la anumite obiecte; această conversie, însă, contrazice disciplina semantică a tastării statice.

Transmiterea

Deoarece obiectivul C permite trimiterea unui mesaj către un obiect care poate să nu răspundă la acesta, obiectul poate gestiona acel mesaj în alte moduri. Unul dintre acestea ar putea fi redirecționarea acestuia (în engleză : forwarding ) către un alt obiect care poate răspunde. Redirecționarea poate fi utilizată pentru a implementa pur și simplu unele modele de proiectare, cum ar fi modelul Observer sau modelul Proxy .

Sistemul de executare Objective C specifică o pereche de metode din clasa Object

  • metode de expediere:
 - ( retval_t ) forward: ( SEL ) sel : ( arglist_t ) args ; // cu GCC
- ( id ) înainte: ( SEL ) sel : ( marg_list ) args ; // cu sisteme NeXT / Apple
  • metode de acțiune:
 - ( retval_t ) performv: ( SEL ) sel : ( arglist_t ) args ; // cu GCC
- ( id ) performv: ( SEL ) sel : ( marg_list ) args ; // cu sisteme NeXT / Apple

iar dacă un obiect dorește să implementeze redirecționarea, trebuie doar să „ suprascrie ” metodele de redirecționare pentru a-și defini comportamentul. Metodele performv:: action nu trebuie să fie suprascrise.

Exemplu

Iată un exemplu de program care ilustrează fundamentele redirecționării.

Expeditor.h
 #import <objc / Object.h>
 
 @interface Forwarder : Obiect
 {
    destinatar id ; // Obiectul către care dorim să redirecționăm mesajul
 }
 
 // Metode de accesorii
 - destinatar ( id ) ;
 - ( void ) setRecipient :( id ) _recipient ; 
 
 @Sfârșit
Expeditor.m
 #import "Forwarder.h"
 
 @implementation Forwarder
 
 - forward : ( SEL ) sel : ( marg_list ) args
 {
    / *
* Verificați dacă destinatarul răspunde efectiv la mesaj.
* Acest lucru poate fi sau nu de dorit, de exemplu, dacă un destinatar
* nu răspunde la mesaj, el poate redirecționa singur mesajul.
* /
    if ([ destinatarul răspunde la : sel ]) 
       return [ destinatar performv : sel : args ];
    altceva
       return [ auto eroare : „Destinatarul nu răspunde” ];
 }
 
 - ( id ) setRecipient : ( id ) _recipient
 {
    recipient = _recipient ;
    întoarce-te de sine ;
 }
 
 - destinatar ( id )
 {
    returnează destinatarul ;
 }
 @Sfârșit
Destinatar.h
 #import <objc / Object.h>
 
 // Un simplu obiect destinatar.
 @interface Destinatar : Obiect
 - ( id ) salut ;
 @Sfârșit
Destinatar.m
 #import "Destinatar.h"
 
 @implementation Destinatar
 
 - ( id ) salut
 {
    printf ( "Destinatarul vă întâmpină! \ n " );
 
    întoarce-te de sine ;
 }
 
 @Sfârșit
principal.m
 #import "Forwarder.h"
 #import "Destinatar.h"
 
 int
 principal ( gol )
 {
    Expeditor * expeditor = [ Expeditor nou ];
    Destinatar * destinatar = [ Destinatar nou ];
 
    [ forwarder setRecipient : recipient ]; // Setați destinatarul
    / *
* Rețineți că „expeditorul” nu răspunde la mesaj!
* Va fi redirecționat. Toate metodele nerecunoscute vor fi
* redirecționat destinatarului (dacă destinatarul le gestionează,
* așa cum se menționează în „Expeditor”).
* /
    [ forwarder salut ]; 
 
    retur 0 ;
 }

Notă

Dacă ar fi să compilăm exemplul, compilatorul ar raporta

 $ gcc -x obiectiv-c -Wno-import Forwarder.m Destinatar.m main.m -lobjc
main.m: În funcția „principal”:
main.m: 12: avertisment: „Expeditorul” nu răspunde la „salut”
$

Compilatorul raportează cele explicate anterior, că Forwarder nu răspunde la mesaj. În unele cazuri, acest semnal poate ajuta la găsirea erorilor, dar în acest caz poate fi ignorat, deoarece redirecționarea a fost implementată. Pentru a rula programul va fi suficient:

 $ ./a.out
Destinatarul salută!

Categorii

Experiența din lumea programării structurate a arătat că una dintre modalitățile de îmbunătățire a structurii codului sursă este descompunerea acestuia în părți mai mici. Pentru a îmbunătăți acest proces, Obiectivul C a introdus conceptul de categorie .

Categoriile vă permit să adăugați metode la o clasă separat. Programatorul poate pune grupuri de metode conexe într-o categorie pentru a le face mai lizibile. De exemplu, puteți crea o categorie de verificare ortografică „pe” un obiect de tip șir pentru a colecta toate metodele de verificare ortografică într-un singur loc.

De asemenea, metodele plasate într-o categorie sunt adăugate la clasă în timpul rulării . În acest fel, categoriile permit programatorului să adauge metode la o clasă existentă fără a fi nevoie de recompilare și fără a avea nevoie de codul sursă al aceleiași clase. În exemplu, dacă sistemul dvs. nu oferă suport pentru verificarea ortografică în implementarea clasei String, îl puteți adăuga fără a-i modifica sursa.

Metodele plasate în categorii fac practic parte din clasă atunci când programul rulează. O categorie accesează, de asemenea, toate variabilele de instanță ale clasei, chiar și cele private.

Categoriile oferă o soluție la problemele „fragilității claselor de bază” în ceea ce privește metodele.

Dacă declarați o metodă într-o categorie cu aceeași semnătură ca o metodă care există deja într-o clasă, se adoptă metoda categoriei. În acest fel, categoriile nu numai că pot adăuga metode la clase, dar pot înlocui și metodele existente. Această caracteristică poate fi utilizată pentru a remedia erorile din alte clase, pur și simplu prin rescrierea metodelor acestora sau pentru a schimba comportamentul unei clase într-un anumit program. Dacă două categorii au metode cu aceeași semnătură, nu este definit ce metodă va executa efectiv.

Diferite limbi au încercat să adauge această caracteristică în diferite moduri. Limbajul TOM a dus conceptul mai departe, permițându-vă, de asemenea, să adăugați variabile. Alte limbi, cum ar fi Sinele , au adoptat în schimb soluții orientate spre prototip ,

Exemplu

Acest exemplu construiește o clasă Integer , definind mai întâi o clasă de bază cu doar câteva metode implementate și apoi adăugând două categorii, Arithmetic și Display , care extind clasa de bază. În timp ce aceste categorii pot accesa variabilele private ale clasei de bază, este o bună practică să accesați aceste variabile prin metode de serviciu care ajută la menținerea categoriilor cât mai independente posibil de clasele pe care le extind. Acesta este un caz tipic de utilizare a categoriilor pentru adăugarea sau modificarea anumitor metode ale clasei de bază (deși nu este considerată o bună practică utilizarea categoriilor pentru a suprascrie subclasele).

Întreg.h
 #include <objc / Object.h>
 
@interface Integer : Object
{
   int întreg ;
}

- ( int ) întreg ;
- ( id ) integer: ( int ) _integer ;
@Sfârșit
Întreg.m
 #import "Integer.h"

@implementation Integer
- ( int ) întreg
{
   returnează numărul întreg ;
}

- ( id ) întreg: ( int ) _integer
{
   integer = _integer ;
   întoarce-te de sine ;
}
@Sfârșit
Aritmetică.h
 #import "Integer.h"

@interface Integer (Arithmetic)
- ( id ) add: ( Integer * ) addend ;
- ( id ) sub: ( Integer * ) subtrahend ;
@Sfârșit
Aritmetica.m
 #import "Arithmetic.h"

@implementation Integer (Aritmetică) 
- ( id ) add: ( Integer * ) addend
{
   return [ self integer : [ self integer ] + [ addend integer ]];
}

- ( id ) sub: ( Integer * ) subtrahend
{
   return [ self integer : [ self integer ] - [ subtrahend integer ]];
}
@Sfârșit
Afișează.h
 #import "Integer.h"

@interface Integer (Display)
- ( id ) showstars ;
- ( id ) showint ;
@Sfârșit
Afișare.m
 #import "Display.h"

@implementation Integer (Display) 
- ( id ) showstars
{
   int i, x = [auto întreg];
   pentru ( i = 0 ; i < x ; i ++ )
      printf ( "*" );
   printf ( " \ n " );

   întoarce-te de sine ;
}

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

   întoarce-te de sine ;
}
@Sfârșit
principal.m
 #import "Integer.h"
#import "Arithmetic.h"
#import "Display.h"

int main ( nul )
{
   Integer * num1 = [ Integer new ], * num2 = [ Integer new ];
   int x ;
   printf ( "Introduceți un număr întreg:" );
   scanf ( "% d" , & x );
   [ num1 întreg : x ];
   [ num1 showstars ];
   printf ( "Introduceți un număr întreg:" );
   scanf ( "% d" , & x );
   [ num2 întreg : x ];
   [ num2 showstars ];

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

Notă

compilarea se face după cum urmează:

 gcc -x obiectiv-c main.m Întreg.m Aritmetică.m Afișaj.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