Obiectiv-C - Objective-C
| Familie | C |
|---|---|
| Proiectat de | Tom Love și Brad Cox |
| A apărut prima dată | 1984 |
| Versiune stabila | 2.0
|
| Disciplina de tastare | static , dinamic , slab |
| OS | Cross-platform |
| Extensii de nume de fișier | .h, .m, .mm, .M |
| Site-ul web | developer.apple.com |
| Implementări majore | |
| Clang , GCC | |
| Influențată de | |
| C , Smalltalk | |
| Influențat | |
| Groovy , Java , Nu , Objective-J , TOM , Swift | |
| |
Objective-C este un limbaj de programare cu scop general , orientat pe obiecte , care adaugă mesaje în stil Smalltalk la limbajul de programare C. Dezvoltat inițial de Brad Cox și Tom Love la începutul anilor 1980, a fost selectat de NeXT pentru sistemul său de operare NeXTSTEP . Objective-C a fost limbajul de programare standard susținut de Apple pentru dezvoltarea aplicațiilor macOS (care provin din NeXTSTEP) și iOS folosind interfețele lor de programare a aplicațiilor (API), Cocoa și Cocoa Touch , până la introducerea Swift în 2014.
Programele Objective-C dezvoltate pentru sisteme de operare non-Apple sau care nu depind de API-urile Apple pot fi, de asemenea, compilate pentru orice platformă acceptată de GNU GCC sau LLVM / Clang .
Fișierele programului „mesagerie / implementare” ale codului sursă Objective-C au de obicei extensii de nume de fișier .m , în timp ce fișierele „antet / interfață” ale Objective-C au extensii .h , la fel ca fișierele antet C. Fișierele Objective-C ++ sunt notate cu o extensie de fișier .mm .
Istorie
Objective-C a fost creat în principal de Brad Cox și Tom Love la începutul anilor 1980 la compania lor Productivity Products International (PPI) .
Conducând la crearea companiei lor, ambii fuseseră introduși în Smalltalk în timp ce se afla la Centrul de tehnologie de programare ITT Corporation în 1981. Cea mai veche lucrare despre Objective-C se remarcă în jurul acelei perioade. Cox a fost intrigat de probleme de reutilizare adevărată în proiectarea și programarea software-ului . El și-a dat seama că un limbaj ca Smalltalk ar fi de neprețuit în construirea mediilor de dezvoltare pentru dezvoltatorii de sisteme de la ITT. Cu toate acestea, el și Tom Love au recunoscut, de asemenea, că compatibilitatea inversă cu C era extrem de importantă în mediul ITT al ingineriei de telecomunicații.
Cox a început să scrie un pre-procesor pentru ca C să adauge unele dintre abilitățile Smalltalk. Curând a avut o implementare funcțională a unei extensii orientate pe obiecte la limbajul C , pe care a numit-o „OOPC” pentru precompilator orientat pe obiecte. Love a fost angajat de Schlumberger Research în 1982 și a avut ocazia să achiziționeze prima copie comercială a Smalltalk-80, care a influențat și mai mult dezvoltarea creării lor. Pentru a demonstra că se pot realiza progrese reale, Cox a arătat că realizarea componentelor software interschimbabile necesită într-adevăr doar câteva modificări practice ale instrumentelor existente. În mod specific, acestea trebuiau să susțină obiectele într-un mod flexibil, să fie furnizate cu un set de biblioteci utilizabile și să permită ca codul (și orice resurse necesare codului) să fie grupate într-un singur format multiplataforma.
Love și Cox au format în cele din urmă PPI pentru a-și comercializa produsul, care a cuplat un compilator Objective-C cu biblioteci de clase. În 1986, Cox a publicat descrierea principală a Objective-C în forma sa originală în cartea Object-Oriented Programming, An Evolutionary Approach . Deși a avut grijă să sublinieze că problema reutilizării are mai mult decât ceea ce oferă Objective-C, limba s-a găsit deseori comparată caracteristică pentru caracteristică cu alte limbi.
Popularizare prin NeXT
În 1988, NeXT a licențiat Objective-C de la StepStone (noul nume al PPI, proprietarul mărcii comerciale Objective-C) și a extins compilatorul GCC pentru a suporta Objective-C. NeXT a dezvoltat bibliotecile AppKit și Foundation Kit pe care s-au bazat interfața de utilizator NeXTSTEP și Interface Builder. În timp ce stațiile de lucru NeXT nu au reușit să aibă un impact mare pe piață, instrumentele au fost apreciate pe scară largă în 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 o platformă pentru programare personalizată.
Pentru a eluda termenii GPL , NeXT intenționase inițial să livreze frontend-ul Objective-C separat, permițându-i utilizatorului să îl conecteze cu GCC pentru a produce executabilul compilatorului. Deși inițial a fost acceptat de Richard M. Stallman , acest plan a fost respins după ce Stallman s-a consultat cu avocații GNU și NeXT a fost de acord să facă Objective-C parte din GCC.
Lucrarea pentru extinderea GCC a fost condusă de Steve Naroff, care s-a alăturat NeXT de la StepStone. Modificările compilatorului au fost puse la dispoziție conform termenilor de licență GPL , dar bibliotecile de rulare nu au fost făcute , făcând contribuția open source inutilizabilă pentru publicul larg. Acest lucru a condus la alte părți care au dezvoltat astfel de biblioteci de rulare sub licență open source. Mai târziu, Steve Naroff a fost, de asemenea, principalul colaborator la Apple pentru a construi frontendul Objective-C pentru Clang .
Proiectul GNU a început să lucreze la implementarea software-ului său liber Cocoa , numit GNUstep , pe baza standardului OpenStep . Dennis Glatting a scris primul timp de execuție GNU Objective-C în 1992. Timpul de execuție GNU Objective-C, care a fost utilizat din 1993, este cel dezvoltat de Kresten Krab Thorup când era student universitar în Danemarca . Thorup a lucrat și la NeXT din 1993 până în 1996.
Dezvoltarea Apple și Swift
După achiziționarea NeXT în 1996, Apple Computer utilizat în OpenStep atunci noul sistem de operare, Mac OS X . Aceasta a inclus Objective-C, instrumentul pentru dezvoltatori bazat pe Objective-C al NeXT, Project Builder și instrumentul său de proiectare a interfeței, Interface Builder . Ambele au fost ulterior îmbinate într-o singură aplicație, Xcode . Majoritatea API- ului actual Cocoa al Apple se bazează pe obiecte de interfață OpenStep și este cel mai semnificativ mediu Objective-C utilizat pentru dezvoltarea activă.
La WWDC 2014, Apple a introdus un nou limbaj, Swift , care a fost caracterizat ca „Obiectiv-C fără C”.
Sintaxă
Objective-C este un strat subțire deasupra lui C și este un „ superset strict ” al lui C, ceea ce înseamnă că este posibil să compilezi orice program C cu un compilator Objective-C și să incluzi liber codul limbajului C într-o clasă Objective-C.
Objective-C își derivă sintaxa obiectului din Smalltalk . Toată sintaxa pentru operațiuni non-orientate spre obiecte (inclusiv variabile primitive, pre-procesare, expresii, declarații de funcții și apeluri de funcții) sunt identice cu cele ale lui C, în timp ce sintaxa pentru caracteristicile orientate pe obiecte este o implementare a Smalltalk- mesagerie stil.
Mesaje
Modelul Objective-C al programării orientate pe obiecte se bazează pe transmiterea mesajelor către instanțe de obiect. În Objective-C nu se apelează o metodă ; se trimite un mesaj . Acest lucru este diferit de modelul de programare în stil Simula folosit de C ++ . Diferența dintre aceste două concepte constă în modul în care codul la care se face referire prin metodă sau numele mesajului este executat. Într-un limbaj în stil Simula, denumirea metodei este legată, în majoritatea cazurilor, de o secțiune de cod din clasa țintă de către compilator. În Smalltalk și Objective-C, ținta unui mesaj este rezolvată la runtime, obiectul receptor însuși interpretând mesajul. O metodă este identificată de un selector sau SEL - un identificator unic pentru fiecare nume de mesaj, de multe ori doar un șir NUL -terminat care îi reprezintă numele - și rezolvat la un indicator de metodă C care îl implementează: un IMP . O consecință a acestui fapt este că sistemul de transmitere a mesajelor nu are verificări de tip. Obiectul către care este direcționat mesajul - receptorul - nu este garantat să răspundă la un mesaj și, dacă nu, creează o excepție.
Trimiterea metodei de mesaj către obiectul indicat de indicatorul obj ar necesita următorul cod în C ++ :
obj->method(argument);
În Objective-C, acest lucru este scris după cum urmează:
[obj method:argument];
Apelul „metodă” este tradus de compilator în familia de funcții runtime objc_msgSend (id self, SEL op, ...) . Diferite implementări gestionează completări moderne, cum ar fi super . În familiile GNU această funcție se numește objc_msg_sendv , dar a fost depreciată în favoarea unui sistem modern de căutare sub objc_msg_lookup .
Ambele stiluri de programare au punctele lor forte și punctele slabe. Programarea orientată pe obiecte în stilul Simula ( C ++ ) permite moștenirea multiplă și executarea mai rapidă utilizând legarea în timp de compilare ori de câte ori este posibil, dar nu acceptă legarea dinamică în mod implicit. De asemenea, forțează toate metodele să aibă o implementare corespunzătoare, cu excepția cazului în care sunt abstracte . Programarea în stil Smalltalk, așa cum este utilizată în Objective-C, permite ca mesajele să rămână neimplementate, metoda fiind rezolvată la implementarea sa în timpul rulării. De exemplu, un mesaj poate fi trimis către o colecție de obiecte, la care se va aștepta ca unii să răspundă, fără teama de a produce erori de rulare. De asemenea, transmiterea mesajului nu necesită definirea unui obiect în timpul compilării. Este încă necesară o implementare pentru ca metoda să fie apelată în obiectul derivat. (Consultați secțiunea de tastare dinamică de mai jos pentru mai multe avantaje ale legării dinamice (tardive).)
Interfețe și implementări
Objective-C necesită ca interfața și implementarea unei clase să fie în blocuri de cod declarate separat. Prin convenție, dezvoltatorii plasează interfața într-un fișier antet și implementarea într-un fișier cod. Fișierele antet, în mod normal cu sufix .h, sunt similare cu fișierele antet C, în timp ce fișierele de implementare (metodă), în mod normal cu sufix .m, pot fi foarte asemănătoare cu fișierele cu cod C.
Interfață
Acest lucru este analog declarațiilor de clasă, așa cum sunt folosite în alte limbaje orientate obiect, cum ar fi C ++ sau Python.
Interfața unei clase este de obicei definită într-un fișier antet. O convenție obișnuită este de a denumi fișierul antet după numele clasei, de exemplu Ball.h ar conține interfața pentru clasa Ball .
O declarație de interfață ia forma:
@interface classname : superclassname {
// instance variables
}
+ classMethod1;
+ (return_type)classMethod2;
+ (return_type)classMethod3:(param1_type)param1_varName;
- (return_type)instanceMethod1With1Parameter:(param1_type)param1_varName;
- (return_type)instanceMethod2With2Parameters:(param1_type)param1_varName
param2_callName:(param2_type)param2_varName;
@end
În cele de mai sus, semnele plus denotă metode de clasă sau metode care pot fi apelate la clasa însăși (nu la o instanță), iar semnele minus denotă metode de instanță , care pot fi apelate numai la o instanță particulară a clasei. De asemenea, metodele de clasă nu au acces la variabilele de instanță .
Codul de mai sus este aproximativ echivalent cu următoarea interfață C ++ :
class classname : public superclassname {
protected:
// instance variables
public:
// Class (static) functions
static void *classMethod1();
static return_type classMethod2();
static return_type classMethod3(param1_type param1_varName);
// Instance (member) functions
return_type instanceMethod1With1Parameter(param1_type param1_varName);
return_type
instanceMethod2With2Parameters(param1_type param1_varName,
param2_type param2_varName = default);
};
Rețineți că instanceMethod2With2Parameters: param2_callName: demonstrează intercalarea segmentelor selector cu expresii de argument, pentru care nu există echivalent direct în C / C ++.
Tipurile de returnare pot fi orice tip C standard , un pointer către un obiect generic Objective-C, un pointer către un anumit tip de obiect precum NSArray *, NSImage * sau NSString * sau un pointer către clasa căreia îi aparține metoda (tip de instanță). Tipul de returnare implicit este ID-ul generic de tip Objective-C .
Argumentele metodei încep cu un nume care etichetează argumentul care face parte din numele metodei, urmat de două puncte urmat de tipul de argument așteptat între paranteze și numele argumentului. Eticheta poate fi omisă.
- (void)setRangeStart:(int)start end:(int)end;
- (void)importDocumentWithName:(NSString *)name
withSpecifiedPreferences:(Preferences *)prefs
beforePage:(int)insertPage;
Un derivat al definiției interfeței este categoria , care permite adăugarea de metode la clasele existente.
Implementare
Interfața declară doar interfața clasei și nu metodele în sine: codul real este scris în fișierul de implementare. Fișierele de implementare (metodă) au în mod normal extensia de fișier .m, care inițial însemna „mesaje”.
@implementation classname
+ (return_type)classMethod {
// implementation
}
- (return_type)instanceMethod {
// implementation
}
@end
Metodele sunt scrise folosind declarațiile de interfață. Comparând Objective-C și C:
- (int)method:(int)i {
return [self square_root:i];
}
int function(int i) {
return square_root(i);
}
Sintaxa permite pseudo- denumirea argumentelor .
- (void)changeColorToRed:(float)red green:(float)green blue:(float)blue {
//... Implementation ...
}
// Called like so:
[myColor changeColorToRed:5.0 green:2.0 blue:6.0];
Reprezentările interne ale unei metode variază între diferite implementări ale Objective-C. Dacă myColor este din clasa Color , metoda instanței -changeColorToRed: verde: albastru: ar putea fi etichetată intern _i_Color_changeColorToRed_green_blue . I este de a se referi la o metodă de exemplu, cu numele metodelor de clasă și apoi anexate și colonurilor schimbat la subliniere. Deoarece ordinea parametrilor face parte din denumirea metodei, aceasta nu poate fi modificată pentru a se potrivi stilului sau expresiei de codare ca și în cazul parametrilor denumiți.
Cu toate acestea, numele interne ale funcției sunt rareori utilizate direct. În general, mesajele sunt convertite în apeluri funcționale definite în biblioteca de runtime Objective-C. Nu se știe neapărat la momentul legăturii ce metodă va fi apelată deoarece clasa receptorului (obiectul care primește mesajul) nu trebuie cunoscută până la runtime.
Instanțierea
Odată ce o clasă Objective-C este scrisă, aceasta poate fi instanțiată. Acest lucru se face prin alocarea mai întâi a unei instanțe neinițializate a clasei (un obiect) și apoi prin inițializarea acesteia. Un obiect nu este complet funcțional până când ambii pași nu sunt finalizați. Acești pași ar trebui să fie realizați cu o singură linie de cod, astfel încât să nu existe niciodată un obiect alocat care nu a fost supus inițializării (și pentru că este neînțelept să păstrăm rezultatul intermediar, deoarece -initpoate returna un obiect diferit de cel pe care este apelat).
Instanțierea cu inițializatorul implicit, fără parametri:
MyObject *foo = [[MyObject alloc] init];
Instanțierea cu un inițializator personalizat:
MyObject *foo = [[MyObject alloc] initWithString:myString];
În cazul în care nu se efectuează nicio inițializare personalizată, metoda „nouă” poate fi adesea utilizată în locul mesajelor de alocare-inițiere:
MyObject *foo = [MyObject new];
De asemenea, unele clase implementează inițializatoare de metode de clasă. De exemplu +new, se combină +allocși -init, dar spre deosebire de +newacestea, returnează o instanță autorizată. Unele inițializatoare de metode de clasă iau parametri:
MyObject *foo = [MyObject object];
MyObject *bar = [MyObject objectWithString:@"Wikipedia :)"];
Aloc mesaj alocari memorie suficientă pentru a păstra toate variabilele de instanță pentru un obiect, setează toate variabilele de instanță la zero valori, și apoi memoria într - o instanță a clasei; memoria nu este în niciun moment din timpul inițializării o instanță a superclasei.
Init Mesajul efectuează set-up al instanței , la creație. Metoda init este adesea scrisă după cum urmează:
- (id)init {
self = [super init];
if (self) {
// perform initialization of object here
}
return self;
}
În exemplul de mai sus, observați idtipul de returnare. Acest tip înseamnă „pointer to any object” în Objective-C (Vezi secțiunea Dynamic typing ).
Modelul inițializatorului este utilizat pentru a se asigura că obiectul este inițializat corect de superclasa sa înainte ca metoda init să efectueze inițializarea. Realizează următoarele acțiuni:
- self = [super init]
- Trimite instanței superclasei un mesaj inițial și atribuie rezultatul către sine (pointer către obiectul curent).
- daca (auto)
- Verifică dacă indicatorul obiect returnat este valid înainte de a efectua orice inițializare.
- întoarce-te
- Returnează valoarea sinelui către apelant.
Un indicator de obiect nevalid are valoarea nil ; declarațiile condiționate precum "if" tratează nil ca un pointer nul, deci codul de inițializare nu va fi executat dacă [super init] returnează zero. Dacă există o eroare la inițializare, metoda init ar trebui să efectueze orice curățare necesară, inclusiv trimiterea unui mesaj de „eliberare” către sine, și să returneze zero pentru a indica că inițializarea a eșuat. Orice verificare pentru astfel de erori trebuie efectuată numai după ce ați apelat inițializarea superclasei pentru a vă asigura că distrugerea obiectului se va face corect.
Dacă o clasă are mai multe metode de inițializare, doar una dintre ele („inițializatorul desemnat”) trebuie să urmeze acest model; alții ar trebui să apeleze inițializatorul desemnat în locul inițializatorului superclasei.
Protocoale
În alte limbaje de programare, acestea se numesc „interfețe”.
Objective-C a fost extins la NeXT pentru a introduce conceptul de moștenire multiplă a specificațiilor, dar nu implementarea, prin introducerea protocoalelor . Acesta este un model realizabil fie ca clasă de bază moștenită abstractă multiplă în C ++ , fie ca „interfață” (ca în Java și C # ). Objective-C folosește protocoale ad hoc numite protocoale informale și protocoale impuse de compilator numite protocoale formale .
Un protocol informal este o listă de metode pe care o clasă poate opta să le implementeze. Este specificat în documentație, deoarece nu are prezență în limbă. Protocoalele informale sunt implementate ca o categorie (vezi mai jos) pe NSObject și includ deseori metode opționale, care, dacă sunt implementate, pot schimba comportamentul unei clase. De exemplu, o clasă de câmp text ar putea avea un delegat care implementează un protocol informal cu o metodă opțională pentru efectuarea completării automate a textului tastat de utilizator. Câmpul text descoperă dacă delegatul implementează acea metodă (prin reflecție ) și, dacă da, apelează metoda delegatului pentru a sprijini caracteristica de completare automată.
Un protocol formal este similar cu o interfață din Java, C # și Ada 2005 . Este o listă de metode pe care orice clasă le poate declara să implementeze. Versiunile lui Objective-C înainte de 2.0 cereau ca o clasă să implementeze toate metodele într-un protocol pe care îl declară adoptând; compilatorul va emite o eroare dacă clasa nu implementează fiecare metodă din protocoalele sale declarate. Objective-C 2.0 a adăugat suport pentru marcarea anumitor metode într-un protocol opțional, iar compilatorul nu va impune implementarea metodelor opționale.
Trebuie declarată o clasă pentru a implementa acel protocol, pentru a se spune că este conform cu acesta. Acest lucru este detectabil în timpul rulării. Protocoalele formale nu pot oferi nicio implementare; pur și simplu îi asigură pe apelanți că clasele care se conformează protocolului vor oferi implementări. În biblioteca NeXT / Apple, protocoalele sunt utilizate frecvent de sistemul de obiecte distribuite pentru a reprezenta abilitățile unui obiect care se execută pe un sistem la distanță.
Sintaxa
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
denotă că există ideea abstractă de blocare. Afirmând în definiția clasei că protocolul este implementat,
@interface NSLock : NSObject <NSLocking>
// ...
@end
instanțele NSLock susțin că vor oferi o implementare pentru cele două metode de instanță.
Tastare dinamică
Objective-C, la fel ca Smalltalk, poate utiliza tastarea dinamică : unui obiect i se poate trimite un mesaj care nu este specificat în interfața sa. Acest lucru poate permite o flexibilitate sporită, deoarece permite unui obiect să „capteze” un mesaj și să trimită mesajul către un alt obiect care poate răspunde la mesaj în mod adecvat sau, de asemenea, să trimită mesajul către alt obiect. Acest comportament este cunoscut sub numele de redirecționare sau delegare de mesaje (vezi mai jos). Alternativ, poate fi utilizat un handler de eroare în cazul în care mesajul nu poate fi redirecționat. Dacă un obiect nu redirecționează un mesaj, nu răspunde la acesta sau nu gestionează o eroare, atunci sistemul va genera o excepție de runtime. Dacă mesajele sunt trimise către zero (indicatorul obiectului nul), acestea vor fi ignorate în tăcere sau vor genera o excepție generică, în funcție de opțiunile compilatorului.
Informațiile de tastare statică pot fi adăugate, de asemenea, opțional la variabile. Aceste informații sunt apoi verificate la compilare. În următoarele patru afirmații, sunt furnizate informații de tip din ce în ce mai specifice. Instrucțiunile sunt echivalente la runtime, dar informațiile suplimentare permit compilatorului să avertizeze programatorul dacă argumentul transmis nu se potrivește cu tipul specificat.
- (void)setMyValue:(id)foo;
În afirmația de mai sus, foo poate fi de orice clasă.
- (void)setMyValue:(id<NSCopying>)foo;
În declarația de mai sus, foo poate fi o instanță a oricărei clase care se conformează NSCopyingprotocolului.
- (void)setMyValue:(NSNumber *)foo;
În declarația de mai sus, foo trebuie să fie o instanță a clasei NSNumber .
- (void)setMyValue:(NSNumber<NSCopying> *)foo;
În declarația de mai sus, foo trebuie să fie o instanță a clasei NSNumber și trebuie să se conformeze NSCopyingprotocolului.
În Objective-C, toate obiectele sunt reprezentate ca indicatori, iar inițializarea statică nu este permisă. Cel mai simplu obiect este tipul către care indică id ( objc_obj * ), care are doar un pointer isa care descrie clasa sa. Alte tipuri din C, cum ar fi valorile și structurile, sunt neschimbate deoarece nu fac parte din sistemul obiectelor. Această decizie diferă de modelul de obiect C ++, unde structurile și clasele sunt unite.
Transmiterea
Objective-C permite trimiterea unui mesaj către un obiect care poate să nu răspundă. În loc să răspundă sau să renunțe la mesaj, un obiect poate redirecționa mesajul către un obiect care poate răspunde. Redirecționarea poate fi utilizată pentru a simplifica implementarea anumitor modele de proiectare , cum ar fi modelul observator sau modelul proxy .
Runtime-ul Objective-C specifică o pereche de metode în Object
- metode de expediere:
- (retval_t)forward:(SEL)sel args:(arglist_t)args; // with GCC - (id)forward:(SEL)sel args:(marg_list)args; // with NeXT/Apple systems
- metode de acțiune:
- (retval_t)performv:(SEL)sel args:(arglist_t)args; // with GCC - (id)performv:(SEL)sel args:(marg_list)args; // with NeXT/Apple systems
Un obiect care dorește să implementeze redirecționarea trebuie doar să suprascrie metoda de redirecționare cu o nouă metodă pentru a defini comportamentul de redirecționare. Metoda de acțiune performv :: nu trebuie să fie ignorată, deoarece această metodă efectuează doar o acțiune bazată pe selector și argumente. Observați SELtipul, care este tipul mesajelor din Objective-C.
Notă: în OpenStep, Cocoa și GNUstep, cadrele utilizate în mod obișnuit ale Objective-C, nu se folosește clasa Object . - (void) forwardInvocation: (NSInvocation *) anInvocation metoda de NSObject clasa este folosit pentru a face redirecționare.
Exemplu
Iată un exemplu de program care demonstrează elementele de bază ale redirecționării.
- Expeditor.h
#import <objc/Object.h>
@interface Forwarder : Object {
id recipient; // The object we want to forward the message to.
}
// Accessor methods.
- (id)recipient;
- (id)setRecipient:(id)_recipient;
@end
- Expeditor.m
#import "Forwarder.h"
@implementation Forwarder
- (retval_t)forward:(SEL)sel args:(arglist_t)args {
/*
* Check whether the recipient actually responds to the message.
* This may or may not be desirable, for example, if a recipient
* in turn does not respond to the message, it might do forwarding
* itself.
*/
if ([recipient respondsToSelector:sel]) {
return [recipient performv:sel args:args];
} else {
return [self error:"Recipient does not respond"];
}
}
- (id)setRecipient:(id)_recipient {
[recipient autorelease];
recipient = [_recipient retain];
return self;
}
- (id)recipient {
return recipient;
}
@end
- Destinatar.h
#import <objc/Object.h>
// A simple Recipient object.
@interface Recipient : Object
- (id)hello;
@end
- Destinatar.m
#import "Recipient.h"
@implementation Recipient
- (id)hello {
printf("Recipient says hello!\n");
return self;
}
@end
- principal.m
#import "Forwarder.h"
#import "Recipient.h"
int main(void) {
Forwarder *forwarder = [Forwarder new];
Recipient *recipient = [Recipient new];
[forwarder setRecipient:recipient]; // Set the recipient.
/*
* Observe forwarder does not respond to a hello message! It will
* be forwarded. All unrecognized methods will be forwarded to
* the recipient
* (if the recipient responds to them, as written in the Forwarder)
*/
[forwarder hello];
[recipient release];
[forwarder release];
return 0;
}
Note
Când este compilat folosind gcc , compilatorul raportează:
$ gcc -x objective-c -Wno-import Forwarder.m Recipient.m main.m -lobjc main.m: In function `main': main.m:12: warning: `Forwarder' does not respond to `hello' $
Compilatorul raportează punctul susținut anterior, că Forwarder nu răspunde la mesajele de salut. În această circumstanță, este sigur să ignorați avertismentul, deoarece redirecționarea a fost implementată. Rularea programului produce această ieșire:
$ ./a.out Recipient says hello!
Categorii
În timpul proiectării Objective-C, una dintre principalele preocupări a fost menținerea bazelor de coduri mari. Experiența din lumea programării structurate a arătat că una dintre principalele modalități de îmbunătățire a codului a fost descompunerea în bucăți mai mici. Objective-C a împrumutat și a extins conceptul de categorii din implementările Smalltalk pentru a ajuta la acest proces.
Mai mult, metodele dintr-o categorie sunt adăugate la o clasă în timpul rulării . Astfel, categoriile permit programatorului să adauge metode unei clase existente - o clasă deschisă - fără a fi nevoie să recompileze acea clasă sau chiar să aibă acces la codul său sursă. De exemplu, dacă un sistem nu conține un verificator ortografic în implementarea lui String, acesta ar putea fi adăugat fără a modifica codul sursă String.
Metodele din categorii devin indistincte de metodele dintr-o clasă atunci când programul este rulat. O categorie are acces complet la toate variabilele de instanță din clasă, inclusiv variabile private.
Dacă o categorie declară o metodă cu aceeași semnătură de metodă ca o metodă existentă într-o clasă, se adoptă metoda categoriei. Astfel, categoriile nu numai că pot adăuga metode la o clasă, ci pot înlocui și metodele existente. Această caracteristică poate fi utilizată pentru a remedia erorile din alte clase, rescriind metodele acestora sau pentru a provoca o schimbare globală a comportamentului unei clase în cadrul unui program. Dacă două categorii au metode cu același nume, dar semnături de metode diferite, nu se definește metoda categoriei care este adoptată.
Alte limbi au încercat să adauge această caracteristică în mai multe moduri. TOM a dus sistemul Objective-C cu un pas mai departe și a permis și adăugarea de variabile. Alte limbi au folosit în schimb soluții bazate pe prototip , cea mai notabilă fiind Sinele .
Limbajele C # și Visual Basic.NET implementează funcționalități superficiale similare sub formă de metode de extensie , dar acestea nu au acces la variabilele private ale clasei. Ruby și alte câteva limbaje dinamice de programare se referă la tehnică drept „ patch-uri de maimuță ”.
Logtalk implementează un concept de categorii (ca entități de primă clasă) care subsumează funcționalitatea categoriilor Objective-C (categoriile Logtalk pot fi utilizate și ca unități de compoziție cu granulație fină atunci când se definesc, de exemplu, clase noi sau prototipuri; în special, o categorie Logtalk poate fi practic importate de orice număr de clase și prototipuri).
Exemplu de utilizare a categoriilor
Acest exemplu creează o clasă Integer , definind mai întâi o clasă de bază cu doar metode de accesor implementate și adăugând două categorii, Aritmetică și Afișare , care extind clasa de bază. În timp ce categoriile pot accesa membrii de date private ai clasei de bază, este adesea o bună practică să accesați acești membri de date private prin metodele de accesor, ceea ce ajută la menținerea categoriilor mai independente de clasa de bază. Implementarea unor astfel de accesori este o utilizare tipică a categoriilor. Un altul este să folosiți categorii pentru a adăuga metode la clasa de bază. Cu toate acestea, nu este considerată o bună practică utilizarea categoriilor pentru suprascrierea subclasei, cunoscută și sub denumirea de patch-uri de maimuță . Protocoalele informale sunt implementate ca o categorie pe clasa de bază NSObject . Prin convenție, fișierele care conțin categorii care extind clasele de bază vor lua numele BaseClass + ExtensionClass.h .
- Întreg.h
#import <objc/Object.h>
@interface Integer : Object {
int integer;
}
- (int)integer;
- (id)integer:(int)_integer;
@end
- Întreg.m
#import "Integer.h"
@implementation Integer
- (int) integer {
return integer;
}
- (id) integer: (int) _integer {
integer = _integer;
return self;
}
@end
- Întreg + Aritmetică.h
#import "Integer.h"
@interface Integer (Arithmetic)
- (id) add: (Integer *) addend;
- (id) sub: (Integer *) subtrahend;
@end
- Întreg + Aritmetică.m
# import "Integer+Arithmetic.h"
@implementation Integer (Arithmetic)
- (id) add: (Integer *) addend {
return [self integer: [self integer] + [addend integer]];
}
- (id) sub: (Integer *) subtrahend {
return [self integer: [self integer] - [subtrahend integer]];
}
@end
- Integer + Display.h
#import "Integer.h"
@interface Integer (Display)
- (id) showstars;
- (id) showint;
@end
- Integer + Display.m
# import "Integer+Display.h"
@implementation Integer (Display)
- (id) showstars {
int i, x = [self integer];
for (i = 0; i < x; i++) {
printf("*");
}
printf("\n");
return self;
}
- (id) showint {
printf("%d\n", [self integer]);
return self;
}
@end
- principal.m
#import "Integer.h"
#import "Integer+Arithmetic.h"
#import "Integer+Display.h"
int main(void) {
Integer *num1 = [Integer new], *num2 = [Integer new];
int x;
printf("Enter an integer: ");
scanf("%d", &x);
[num1 integer:x];
[num1 showstars];
printf("Enter an integer: ");
scanf("%d", &x);
[num2 integer:x];
[num2 showstars];
[num1 add:num2];
[num1 showint];
return 0;
}
Note
Compilarea se realizează, de exemplu, prin:
gcc -x objective-c main.m Integer.m Integer+Arithmetic.m Integer+Display.m -lobjc
Se poate experimenta lăsând în afară liniile #import "Integer + Arithmetic.h" și [num1 add: num2] și omițând Integer + Arithmetic.m în compilație. Programul va rula în continuare. Aceasta înseamnă că este posibil să combinați și să potriviți categorii adăugate, dacă este necesar; dacă o categorie nu trebuie să aibă o anumită abilitate, pur și simplu nu poate fi compilată în.
Pozând
Objective-C permite unei clase să înlocuiască în totalitate o altă clasă în cadrul unui program. Se spune că clasa de înlocuire „pozează ca” clasa țintă.
Poziția de clasă a fost declarată depreciată cu Mac OS X v10.5 și nu este disponibilă în runtime pe 64 de biți. Funcționalitate similară poate fi obținută prin utilizarea metodei swizzling în categorii, care schimbă implementarea unei metode cu alta care are aceeași semnătură.
Pentru versiunile care încă acceptă poza, toate mesajele trimise către clasa țintă sunt primite în schimb de clasa poză. Există mai multe restricții:
- O clasă poate poziționa doar ca una dintre superclasele sale directe sau indirecte.
- Clasa de poziționare nu trebuie să definească nicio variabilă de instanță nouă care este absentă din clasa țintă (deși poate defini sau suprascrie metodele).
- Este posibil ca clasa țintă să nu fi primit niciun mesaj înainte de pozare.
Poziția, în mod similar cu categoriile, permite mărirea globală a claselor existente. Poziția permite două funcții absente din categorii:
- O clasă de pozare poate apela la metode suprascrise prin super, încorporând astfel implementarea clasei țintă.
- O clasă de pozare poate suprascrie metodele definite în categorii.
De exemplu,
@interface CustomNSApplication : NSApplication
@end
@implementation CustomNSApplication
- (void) setMainMenu: (NSMenu*) menu {
// do something with menu
}
@end
class_poseAs ([CustomNSApplication class], [NSApplication class]);
Aceasta interceptează fiecare invocație a setMainMenu către NSApplication.
#import
În limbajul C, #includedirectiva de precompilare face ca conținutul unui fișier să fie inserat în sursă în acel moment. Obiectivul-C are #importdirectiva, echivalentă, cu excepția faptului că fiecare fișier este inclus o singură dată pentru fiecare unitate de compilare, evitând necesitatea de a include paznici .
Compilație Linux gcc
// FILE: hello.m
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
/* my first program in Objective-C */
NSLog(@"Hello, World! \n");
return 0;
}
# Compile Command Line for gcc and MinGW Compiler:
$ gcc \
$(gnustep-config --objc-flags) \
-o hello \
hello.m \
-L /GNUstep/System/Library/Libraries \
-lobjc \
-lgnustep-base
$ ./hello
Alte caracteristici
Caracteristicile Objective-C permit adesea soluții flexibile și adesea ușoare la problemele de programare.
- Delegarea metodelor către alte obiecte și invocarea la distanță pot fi implementate cu ușurință folosind categorii și redirecționarea mesajelor.
- Swizzling de isa pointer permite clase să se schimbe în timpul rulării. De obicei utilizat pentru depanare în cazul în care obiectele eliberate sunt transformate în obiecte zombie al căror singur scop este de a raporta o eroare atunci când cineva le sună. Swizzling a fost, de asemenea, utilizat în Enterprise Objects Framework pentru a crea defecțiuni în baza de date. Swizzling este folosit astăzi de Fundația Apple Framework pentru a implementa Key-Value Observing .
Variante lingvistice
Obiectiv-C ++
Objective-C ++ este o variantă de limbaj acceptată de front-end la GNU Compiler Collection și Clang , care poate compila fișiere sursă care utilizează o combinație de sintaxă C ++ și Objective-C. Objective-C ++ adaugă la C ++ extensiile pe care Objective-C le adaugă lui C. Deoarece nu se face nimic pentru a unifica semantica din spatele diferitelor caracteristici ale limbajului, se aplică anumite restricții:
- O clasă C ++ nu poate deriva dintr-o clasă Objective-C și invers.
- Spațiile de nume C ++ nu pot fi declarate într-o declarație Objective-C.
- Declarațiile Objective-C pot apărea numai în domeniul global, nu într-un spațiu de nume C ++
- Clasele Objective-C nu pot avea variabile de instanță ale claselor C ++ care nu au un constructor implicit sau care au una sau mai multe metode virtuale , dar indicatoarele către obiectele C ++ pot fi utilizate ca variabile de instanță fără restricții (alocați-le cu noi în metoda -init).
- Semantica C ++ „după valoare” nu poate fi aplicată obiectelor Objective-C, care sunt accesibile doar prin pointeri.
- An Objective-C declaration cannot be within a C++ template declaration and vice versa. However, Objective-C types (e.g.,
Classname *) can be used as C++ template parameters. - Tratarea excepțiilor Objective-C și C ++ este distinctă; gestionarii fiecăruia nu pot gestiona excepții de celălalt tip. Ca urmare, distructorii de obiecte nu sunt executați. Acest lucru este atenuat în timpii de execuție recenți "Objective-C 2.0", deoarece excepțiile Objective-C sunt fie înlocuite complet cu excepții C ++ (runtime Apple), fie parțial atunci când biblioteca Objective-C ++ este legată (GNUstep libobjc2).
- Blocurile Objective-C și lambda C ++ 11 sunt entități distincte. Cu toate acestea, un bloc este generat în mod transparent pe macOS atunci când treceți un lambda unde este așteptat un bloc.
Obiectiv-C 2.0
La Conferința mondială pentru dezvoltatori din 2006 , Apple a anunțat lansarea „Objective-C 2.0”, o revizuire a limbajului Objective-C pentru a include „colectarea modernă a gunoiului, îmbunătățiri ale sintaxei, îmbunătățiri ale performanței în timpul rulării și suport pe 64 de biți”. Mac OS X v10.5 , lansat în octombrie 2007, a inclus un compilator Objective-C 2.0. GCC 4.6 acceptă multe caracteristici noi Objective-C, cum ar fi proprietăți declarate și sintetizate, sintaxa punctelor, enumerare rapidă, metode opționale de protocol, atribute metodă / protocol / clasă, extensii de clasă și un nou API de execuție GNU Objective-C.
Denumirea Objective-C 2.0 reprezintă o pauză în sistemul de versiuni al limbajului, deoarece ultima versiune Objective-C pentru NeXT a fost „objc4”. Numele acestui proiect a fost păstrat în ultima versiune a codului sursă de runtime obișnuit Objective-C în Mac OS X Leopard (10.5).
Colectarea gunoiului
Objective-C 2.0 a furnizat un colector de gunoi generativ opțional, conservator . Când se execută în modul compatibil înapoi , timpul de execuție a transformat operațiunile de numărare a referințelor , cum ar fi „păstrare” și „eliberare” în no-ops . Toate obiectele au fost supuse colectării gunoiului atunci când a fost activată colectarea gunoiului. Indicatorii C obișnuiți ar putea fi calificați cu „__strong” pentru a declanșa interceptările de bază ale compilatorului de barieră la scriere și astfel să participe la colectarea gunoiului. Un subsistem slab zero-zero a fost, de asemenea, furnizat astfel încât indicatoarele marcate ca „__ slab” sunt setate la zero atunci când obiectul (sau mai simplu, memoria GC) este colectat. Colectorul de gunoi nu există în implementarea iOS a Objective-C 2.0. Colectarea deșeurilor din Objective-C rulează pe un fir de fundal cu prioritate redusă și se poate opri la evenimentele utilizatorului, cu intenția de a păstra experiența utilizatorului receptivă.
Colectarea gunoiului a fost depreciată în Mac OS X v10.8 în favoarea numărării automate a referințelor (ARC). Objective-C pe iOS 7 care rulează pe ARM64 folosește 19 biți dintr-un cuvânt pe 64 de biți pentru a stoca numărul de referințe, ca o formă de pointeri etichetați .
Proprietăți
Objective-C 2.0 introduce o nouă sintaxă pentru a declara variabilele de instanță ca proprietăți , cu atribute opționale pentru a configura generarea metodelor de acces. Proprietățile sunt, într-un anumit sens, variabile de instanță publică; adică declararea unei variabile de instanță ca proprietate oferă claselor externe acces (posibil limitat, de exemplu numai citire) la acea proprietate. O proprietate poate fi declarată ca „numai citită” și poate fi prevăzută cu semantică de stocare precum assign, copysau retain. În mod implicit, sunt luate în considerare proprietățile atomic, ceea ce duce la o blocare care împiedică mai multe fire de acces să le acceseze în același timp. O proprietate poate fi declarată ca nonatomic, care elimină această blocare.
@interface Person : NSObject {
@public
NSString *name;
@private
int age;
}
@property(copy) NSString *name;
@property(readonly) int age;
- (id)initWithAge:(int)age;
@end
Proprietățile sunt implementate prin intermediul @synthesizecuvântului cheie, care generează metode getter (și setter, dacă nu numai în citire) conform declarației de proprietate. Alternativ, metodele getter și setter trebuie să fie implementate în mod explicit, sau @dynamiccuvântul cheie poate fi folosit pentru a indica faptul că metodele accesorului vor fi furnizate prin alte mijloace. Când sunt compilate folosind clang 3.1 sau o versiune ulterioară, toate proprietățile care nu sunt declarate în mod explicit cu @dynamic, marcate readonlysau au getter și setter complet implementate de utilizator vor fi implicit implicite @synthesize.
@implementation Person
@synthesize name;
- (id)initWithAge:(int)initAge {
self = [super init];
if (self) {
// NOTE: direct instance variable assignment, not property setter
age = initAge;
}
return self;
}
- (int)age {
return age;
}
@end
Proprietățile pot fi accesate folosind sintaxa tradițională de trecere a mesajelor, notația punctului sau, în codare valoare-cheie, după nume prin metodele „valueForKey:” / „setValue: forKey:”.
Person *aPerson = [[Person alloc] initWithAge:53];
aPerson.name = @"Steve"; // NOTE: dot notation, uses synthesized setter,
// equivalent to [aPerson setName: @"Steve"];
NSLog(@"Access by message (%@), dot notation(%@), property name(% @) and "
"direct instance variable access(% @) ",
[aPerson name],
aPerson.name, [aPerson valueForKey:@"name"], aPerson -> name);
Pentru a utiliza notația punct pentru a invoca accesorii de proprietăți în cadrul unei metode de instanță, trebuie utilizat cuvântul cheie „auto”:
- (void)introduceMyselfWithProperties:(BOOL)useGetter {
NSLog(@"Hi, my name is %@.", (useGetter ? self.name : name));
// NOTE: getter vs. ivar access
}
Proprietățile unei clase sau a unui protocol pot fi introspectate dinamic .
int i;
int propertyCount = 0;
objc_property_t *propertyList =
class_copyPropertyList([aPerson class], &propertyCount);
for (i = 0; i < propertyCount; i++) {
objc_property_t *thisProperty = propertyList + i;
const char *propertyName = property_getName(*thisProperty);
NSLog(@"Person has a property: '%s'", propertyName);
}
Variabile de instanță non-fragile
Objective-C 2.0 oferă variabile de instanță non-fragile acolo unde sunt acceptate de runtime (adică atunci când se construiește codul pentru MacOS pe 64 de biți și pentru toate iOS). În timpul runtime-ului modern, un strat suplimentar de indirectare este adăugat accesului variabil de instanță, permițând linkerului dinamic să regleze aspectul instanței la runtime. Această caracteristică permite două îmbunătățiri importante ale codului Objective-C:
- Elimină problema fragilă a interfeței binare ; superclasele pot schimba dimensiunile fără a afecta compatibilitatea binară.
- Permite variabilelor de instanță care oferă suportul pentru proprietăți să fie sintetizate la runtime fără ca acestea să fie declarate în interfața clasei.
Enumerare rapidă
În loc să utilizeze un obiect sau indici NSEnumerator pentru a itera printr-o colecție, Objective-C 2.0 oferă sintaxa rapidă de enumerare. În Objective-C 2.0, următoarele bucle sunt funcțional echivalente, dar au trăsături de performanță diferite.
// Using NSEnumerator
NSEnumerator *enumerator = [thePeople objectEnumerator];
Person *p;
while ((p = [enumerator nextObject]) != nil) {
NSLog(@"%@ is %i years old.", [p name], [p age]);
}
// Using indexes
for (int i = 0; i < [thePeople count]; i++) {
Person *p = [thePeople objectAtIndex:i];
NSLog(@"%@ is %i years old.", [p name], [p age]);
}
// Using fast enumeration
for (Person *p in thePeople) {
NSLog(@"%@ is %i years old.", [p name], [p age]);
}
Enumerarea rapidă generează un cod mai eficient decât enumerarea standard, deoarece apelurile de metodă pentru enumerarea peste obiecte sunt înlocuite cu aritmetica indicatorului folosind protocolul NSFastEnumeration.
Extensii de clasă
O extensie de clasă are aceeași sintaxă ca o declarație de categorie fără nume de categorie, iar metodele și proprietățile declarate în ea sunt adăugate direct la clasa principală. Este folosit mai ales ca alternativă la o categorie pentru a adăuga metode la o clasă fără a le face publicitate în anteturile publice, cu avantajul că pentru extensiile de clasă compilatorul verifică dacă toate metodele declarate în mod privat sunt implementate.
Implicații pentru dezvoltarea cacao
Toate aplicațiile Objective-C dezvoltate pentru macOS care utilizează îmbunătățirile de mai sus pentru Objective-C 2.0 sunt incompatibile cu toate sistemele de operare înainte de 10.5 (Leopard). Deoarece enumerarea rapidă nu generează exact aceleași binare ca enumerarea standard, utilizarea acesteia va face ca o aplicație să se blocheze pe Mac OS X versiunea 10.4 sau mai devreme.
Blocuri
Blocks este o extensie non-standard pentru Objective-C (și C și C ++ ) care utilizează sintaxa specială pentru a crea închideri . Blocurile sunt acceptate numai în Mac OS X 10.6 „Snow Leopard” sau o versiune ulterioară, iOS 4 sau o versiune ulterioară și GNUstep cu libobjc2 1.7 și compilarea cu clang 3.1 sau o versiune ulterioară.
#include <stdio.h>
#include <Block.h>
typedef int (^IntBlock)();
IntBlock MakeCounter(int start, int increment) {
__block int i = start;
return Block_copy( ^ {
int ret = i;
i += increment;
return ret;
});
}
int main(void) {
IntBlock mycounter = MakeCounter(5, 2);
printf("First call: %d\n", mycounter());
printf("Second call: %d\n", mycounter());
printf("Third call: %d\n", mycounter());
/* because it was copied, it must also be released */
Block_release(mycounter);
return 0;
}
/* Output:
First call: 5
Second call: 7
Third call: 9
*/
Obiectiv modern-C
Apple a adăugat de-a lungul timpului câteva caracteristici suplimentare la Objective 2.0. Adăugările se aplică numai „ compilatorului Apple LLVM ”, adică clang frontend al limbii. În mod confuz, versiunea utilizată de Apple diferă de cea a LLVM în amonte; consultați versiunile Xcode § Toolchain pentru o traducere la numerele de versiune LLVM open source.
Numărare automată a referințelor
Numărarea automată a referințelor (ARC) este o caracteristică în timp de compilare care elimină necesitatea programatorilor de a gestiona manual numărul de rețineri folosind retainși release. Spre deosebire de colectarea gunoiului , care are loc în timpul rulării, ARC elimină cheltuielile generale ale unui proces separat care gestionează numărul de rețineri. ARC și gestionarea manuală a memoriei nu se exclud reciproc; programatorii pot continua să utilizeze codul care nu este ARC în proiectele cu ARC dezactivând ARC pentru fișiere de cod individuale. Xcode poate încerca, de asemenea, să actualizeze automat un proiect la ARC.
ARC a fost introdus în LLVM 3.0. Acest lucru se traduce prin Xcode 4.2 (2011) sau compilatorul Apple LLVM 3.0.
Litere
Runtime-urile NeXT și Apple Obj-C au inclus mult timp o modalitate scurtă de a crea șiruri noi, utilizând sintaxa literală @"a new string"sau de a scădea la constantele CoreFoundation kCFBooleanTrueși kCFBooleanFalsepentru NSNumbervalori booleene. Utilizarea acestui format salvează programatorul de a fi nevoit să utilizeze initWithStringmetode mai lungi sau similare atunci când face anumite operații.
Atunci când se utilizează Apple a LLVM compilator 4.0 (Xcode 4.4) sau mai târziu, tablouri, dicționare, și numere ( NSArray, NSDictionary, NSNumberclase) pot fi create folosind sintaxa literală în loc de metode. (Compilatorul Apple LLVM 4.0 se traduce în open source LLVM și Clang 3.1.)
Exemplu fără litere:
NSArray *myArray = [NSArray arrayWithObjects:object1,object2,object3,nil];
NSDictionary *myDictionary1 = [NSDictionary dictionaryWithObject:someObject forKey:@"key"];
NSDictionary *myDictionary2 = [NSDictionary dictionaryWithObjectsAndKeys:object1, key1, object2, key2, nil];
NSNumber *myNumber = [NSNumber numberWithInt:myInt];
NSNumber *mySumNumber= [NSNumber numberWithInt:(2 + 3)];
NSNumber *myBoolNumber = [NSNumber numberWithBool:YES];
Exemplu cu litere:
NSArray *myArray = @[ object1, object2, object3 ];
NSDictionary *myDictionary1 = @{ @"key" : someObject };
NSDictionary *myDictionary2 = @{ key1: object1, key2: object2 };
NSNumber *myNumber = @(myInt);
NSNumber *mySumNumber = @(2+3);
NSNumber *myBoolNumber = @YES;
NSNumber *myIntegerNumber = @8;
Cu toate acestea, diferit de literele șirului , care se compilează la constante din executabil, acești literali se compilează la cod echivalent cu apelurile metodei de mai sus. În special, în gestionarea memoriei cu referință manuală, aceste obiecte sunt eliberate automat, ceea ce necesită îngrijire suplimentară atunci când, de exemplu, sunt utilizate cu variabile statice funcționale sau cu alte tipuri de globale.
Subscriere
Când utilizați compilatorul Apple LLVM 4.0 sau o versiune ulterioară, tablourile și dicționarele ( NSArrayși NSDictionaryclasele) pot fi manipulate folosind subscripting. Subscrierea poate fi utilizată pentru a extrage valori din indici (matrice) sau chei (dicționar), iar cu obiecte mutabile, poate fi folosită și pentru a seta obiecte pe indici sau chei. În cod, subscrierea este reprezentată folosind paranteze [ ].
Exemplu fără subscriere:
id object1 = [someArray objectAtIndex:0];
id object2 = [someDictionary objectForKey:@"key"];
[someMutableArray replaceObjectAtIndex:0 withObject:object3];
[someMutableDictionary setObject:object4 forKey:@"key"];
Exemplu cu subscriere:
id object1 = someArray[0];
id object2 = someDictionary[@"key"];
someMutableArray[0] = object3;
someMutableDictionary[@"key"] = object4;
Sintaxa "modernă" Objective-C (1997)
După achiziționarea NeXT de către Apple, s-a încercat ca limbajul să fie mai acceptabil pentru programatorii mai familiarizați cu Java decât Smalltalk. Una dintre aceste încercări a fost introducerea a ceea ce în acel moment a fost numită „Sintaxă modernă” pentru Objective-C (spre deosebire de sintaxa actuală, „clasică”). Nu a existat nicio schimbare în comportament, aceasta a fost doar o sintaxă alternativă. În loc să scrieți o invocare de metodă ca
object = [[MyClass alloc] init];
[object firstLabel: param1 secondLabel: param2];
În schimb, a fost scris ca
object = (MyClass.alloc).init;
object.labels ( param1, param2 );
În mod similar, declarațiile au trecut de la formular
-(void) firstLabel: (int)param1 secondLabel: (int)param2;
la
-(void) labels ( int param1, int param2 );
Această sintaxă „modernă” nu mai este acceptată în dialectele actuale ale limbajului Objective-C.
mulle-objc
Proiectul mulle-objc este o altă re-implementare a Objective-C. Suportă compilatoare GCC sau Clang / LLVM ca backend. Acesta diferă de alte runtime în ceea ce privește sintaxa, semantica și compatibilitatea ABI. Suportă Linux, FreeBSD și Windows.
Compilator de obiecte portabile
Pe lângă implementarea GCC / NeXT / Apple , care a adăugat mai multe extensii la implementarea originală Stepstone , există și o altă implementare gratuită, open-source Objective-C numită Portable Object Compiler. Setul de extensii implementat de Portable Object Compiler diferă de implementarea GCC / NeXT / Apple; în special, include blocuri de tip Smalltalk pentru Objective-C, în timp ce îi lipsesc protocoale și categorii, două caracteristici utilizate pe scară largă în OpenStep și derivatele și rudele sale. În general, POC reprezintă o etapă mai veche, pre-NEXT în evoluția limbajului, aproximativ conformă cu cartea lui Brad Cox din 1991.
De asemenea, include o bibliotecă runtime numită ObjectPak, care se bazează pe biblioteca originală ICPak101 a lui Cox (care la rândul său derivă din biblioteca clasei Smalltalk-80) și este destul de radical diferită de OpenStep FoundationKit.
Obiectivul GEOS-C
Sistemul PC GEOS a folosit un limbaj de programare cunoscut sub numele de GEOS Objective-C sau goc ; în ciuda asemănării numelui, cele două limbi sunt similare numai în conceptul general și utilizarea cuvintelor cheie prefixate cu un semn @.
Zăngăni
Zăngănit Compilatorul, parte a LLVM proiectului, implementează Objective-C și alte limbi. După ce GCC 4.3 (2008) a trecut la GPLv3, Apple l-a abandonat în favoarea clangului, un compilator pe care are mai multă putere legală să îl modifice. Drept urmare, multe dintre caracteristicile moderne ale limbajului Objective-C sunt acceptate doar de Clang.
Schema de versiuni Apple pentru „compilatorul LLVM” bazat pe clang diferă de versiunea open-source a LLVM. Consultați versiunile Xcode § Toolchain pentru o traducere
GNU, GNUstep și WinObjC
Proiectul GNU a fost, de multă vreme, interesat de o platformă de portare a programelor NeXT și Obj-C. ChangeLog pentru directorul libobjc din GCC sugerează că acesta a existat înainte de 1998 (GCC 2.95), iar README-ul său indică în continuare o rescriere în 1993 (GCC 2.4).
Codul sursă NeXT frontend a fost lansat, deoarece a fost creat ca parte a GCC, a lansat GNU Public License, care îi obligă pe cei care fac lucrări derivate să facă acest lucru. Apple a continuat această tradiție în lansarea furcii GCC până la 4.2.1, după care au abandonat compilatorul. Mentenanții GCC au acceptat schimbările, dar nu au investit prea mult în susținerea unor funcții mai noi, cum ar fi limbajul Objective-C 2.0.
Dezvoltatorii GNUstep, interesați de noua limbă, au furnizat GCC libobjc către un proiect independent de GCC numit libobjc2 în 2009. Au aranjat, de asemenea, ca timpul de rulare să fie folosit cu Clang pentru a profita de noua sintaxă a limbajului. GCC s-a deplasat încet în același timp, dar la GCC 4.6.0 (2011) au trecut la Objective-C 2.0 și în libobjc. Documentația GNUstep sugerează că implementarea GCC încă nu are suport pentru blocuri, variabile non-fragile și ARC mai nou.
Microsoft a introdus libobjc2 într-o parte a WinObjC , podul iOS pentru platforma Windows universală , în 2015. Combinat cu propria sa implementare Cocoa Touch și API-urile subiacente, proiectul permite reutilizarea codului aplicației iOS în cadrul aplicațiilor UWP.
Pe Windows, instrumentele de dezvoltare Objective-C sunt furnizate pentru descărcare pe site-ul web al GNUStep. Sistemul de dezvoltare GNUStep constă din următoarele pachete: GNUstep MSYS System, GNUstep Core, GNUstep Devel, GNUstep Cairo, ProjectCenter IDE (Like Xcode, dar nu la fel de complex), Gorm (Interface Builder Like Xcode NIB builder). Acești instalatori binari nu au fost actualizați din 2016, deci ar putea fi o idee mai bună să instalați doar construind sub Cygwin sau MSYS2 .
Utilizarea bibliotecii
Objective-C astăzi este adesea folosit în tandem cu o bibliotecă fixă de obiecte standard (adesea cunoscută sub numele de „kit” sau „cadru”), cum ar fi Cocoa , GNUstep sau ObjFW . Aceste biblioteci vin adesea cu sistemul de operare: bibliotecile GNUstep vin adesea cu distribuții bazate pe Linux , iar Cocoa vine cu macOS. Programatorul nu este obligat să moștenească funcționalitatea din clasa de bază existentă (NSObject / OFObject). Objective-C permite declararea de noi clase rădăcină care nu moștenesc nicio funcționalitate existentă. Inițial, mediile de programare bazate pe Objective-C ofereau de obicei o clasă Object ca clasă de bază de la care au moștenit aproape toate celelalte clase. Odată cu introducerea OpenStep, NeXT a creat o nouă clasă de bază numită NSObject, care oferea caracteristici suplimentare asupra obiectului (un accent pe utilizarea referințelor obiectelor și a numărării referințelor în loc de indicatori brut, de exemplu). Aproape toate clasele din Cocoa moștenesc de la NSObject.
Redenumirea nu numai că a diferențiat noul comportament implicit al claselor în cadrul OpenStep API, dar a permis codului care folosea Object - clasa de bază originală utilizată pe NeXTSTEP (și, mai mult sau mai puțin, alte biblioteci de clase Objective-C) - coexistă în același timp de rulare cu codul care a folosit NSObject (cu unele limitări). Introducerea prefixului celor două litere a devenit, de asemenea, o formă simplistă de spații de nume, care nu are în Objective-C. Folosirea unui prefix pentru a crea un identificator de ambalaj informal a devenit un standard de codare informal în comunitatea Objective-C și continuă până în prezent.
Mai recent, au început să apară manageri de pachete, cum ar fi CocoaPods , care își propune să fie atât un manager de pachete, cât și un depozit de pachete. O mulțime de cod open-source Objective-C care a fost scris în ultimii ani poate fi acum instalat folosind CocoaPods.
Analiza limbajului
Implementările Objective-C utilizează un sistem de rulare subțire scris în C, care adaugă puțin la dimensiunea aplicației. În schimb, majoritatea sistemelor orientate obiect la momentul creării foloseau runtime mari ale mașinilor virtuale . Programele scrise în Objective-C tind să nu fie cu mult mai mari decât dimensiunea codului lor și a bibliotecilor (care, în general, nu trebuie incluse în distribuția software-ului), spre deosebire de sistemele Smalltalk unde o cantitate mare de memorie era folosit doar pentru a deschide o fereastră. Aplicațiile Objective-C tind să fie mai mari decât aplicațiile C sau C ++ similare, deoarece tastarea dinamică Objective-C nu permite eliminarea sau înclinarea metodelor. Deoarece programatorul are o astfel de libertate de a delega, redirecționa apelurile, de a crea selectoare din mers și de a le transmite sistemului de execuție, compilatorul Objective-C nu poate presupune că este sigur să eliminați metodele neutilizate sau să introduceți apeluri.
În mod similar, limbajul poate fi implementat deasupra compilatoarelor C existente (în GCC , mai întâi ca preprocesator, apoi ca modul), mai degrabă decât ca un nou compilator. Acest lucru permite Objective-C , să impulsioneze o colecție uriașă existentă de cod C, biblioteci, instrumente, etc. bibliotecile C existente pot fi învelite în Objective-C ambalaje pentru a oferi o interfață de tip OO. În acest aspect, este similar cu biblioteca GObject și limbajul Vala , care sunt utilizate pe scară largă în dezvoltarea aplicațiilor GTK .
Toate aceste schimbări practice au redus bariera la intrare , probabil cea mai mare problemă pentru acceptarea pe scară largă a Smalltalk în anii 1980.
O critică obișnuită este că Objective-C nu are suport lingvistic pentru spațiile de nume . În schimb, programatorii sunt obligați să adauge prefixe la numele clasei lor, care sunt în mod tradițional mai scurte decât numele spațiilor de nume și, prin urmare, sunt mai predispuși la coliziuni. Începând cu 2007, toate clasele și funcțiile macOS din mediul de programare Cocoa sunt prefixate cu „NS” (de ex. NSObject, NSButton) pentru a le identifica ca aparținând nucleului macOS sau iOS; „NS” derivă din numele claselor definite în timpul dezvoltării NeXTSTEP .
Deoarece Objective-C este un superset strict al lui C, nu tratează tipurile C primitive ca obiecte de primă clasă .
Spre deosebire de C ++ , Objective-C nu acceptă supraîncărcarea operatorului . De asemenea, spre deosebire de C ++, Objective-C permite unui obiect să moștenească direct doar dintr-o singură clasă (interzicând moștenirea multiplă ). Cu toate acestea, în majoritatea cazurilor, categoriile și protocoalele pot fi utilizate ca modalități alternative de a obține aceleași rezultate.
Deoarece Objective-C folosește tastarea dinamică a runtime și pentru că toate apelurile de metodă sunt apeluri de funcții (sau, în unele cazuri, syscalls), multe optimizări de performanță obișnuite nu pot fi aplicate metodelor Objective-C (de exemplu: inlining, propagare constantă, optimizări interprocedurale, și înlocuirea scalară a agregatelor). Acest lucru limitează performanța abstracțiilor Objective-C în raport cu abstracții similare în limbaje precum C ++, unde astfel de optimizări sunt posibile.
Gestionarea memoriei
Primele versiuni ale Objective-C nu au acceptat colectarea gunoiului . La vremea respectivă, această decizie era o chestiune de dezbatere și mulți oameni considerau „perioade moarte” lungi (când Smalltalk a efectuat colectarea) pentru a face întregul sistem inutilizabil. Unele implementări terță parte au adăugat această caracteristică (mai ales GNUstep folosind Boehm ), iar Apple a implementat-o începând cu Mac OS X v10.5 . Cu toate acestea, în versiunile mai recente de macOS și iOS, colectarea gunoiului a fost depreciată în favoarea numărării automate a referințelor (ARC), introdusă în 2011.
Cu ARC, inserțiile compilatorului rețin și eliberează automat apelurile în codul Objective-C pe baza analizei codului static . Automatul scutește programatorul de a scrie în codul de gestionare a memoriei. ARC adaugă, de asemenea, referințe slabe la limbajul Objective-C.
Diferențe filosofice între Objective-C și C ++
Proiectarea și implementarea C ++ și Objective-C reprezintă abordări fundamental diferite de extindere a C.
În plus față de stilul de programare C procedurale, a C ++ sprijină în mod direct anumite forme de programare orientate-obiect , programarea generică , și metaprogramarea . C ++ vine, de asemenea, cu o bibliotecă standard mare, care include mai multe clase de containere . În mod similar, Objective-C , adaugă programarea orientată pe obiecte , dactilografiere dinamice , și de reflecție la C. Obiectiv-C nu furnizează o bibliotecă standard de sine , dar în cele mai multe locuri în care este utilizat Objective-C, este utilizat cu un OpenStep -like bibliotecă cum ar fi OPENSTEP , Cocoa sau GNUstep , care oferă funcționalități similare bibliotecii standard C ++.
O diferență notabilă este că Objective-C oferă suport pentru runtime pentru caracteristicile reflexive , în timp ce C ++ adaugă doar o cantitate mică de suport pentru runtime la C. În Objective-C, un obiect poate fi întrebat despre propriile sale proprietăți, de exemplu, dacă va răspunde la un anumit mesaj. În C ++, acest lucru nu este posibil fără utilizarea bibliotecilor externe.
Utilizarea reflecției face parte din distincția mai largă dintre caracteristicile dinamice (timp de rulare) și caracteristicile statice (timp de compilare) ale unui limbaj. Deși Objective-C și C ++ utilizează fiecare un amestec de ambele caracteristici, Objective-C este orientat în mod hotărât spre decizii în timpul rulării, în timp ce C ++ este orientat spre decizii în timp de compilare. Tensiunea dintre programarea dinamică și statică implică multe dintre compromisurile clasice în programare: caracteristicile dinamice adaugă flexibilitate, caracteristicile statice adaugă viteza și verificarea tipului.
Programarea generică și metaprogramarea pot fi implementate în ambele limbi folosind polimorfismul de rulare . În C ++, acesta ia forma funcțiilor virtuale și a identificării tipului de rulare , în timp ce Objective-C oferă tastare dinamică și reflectare. Atât Objective-C cât și C ++ acceptă polimorfismul în timp de compilare ( funcții generice ), Objective-C adăugând această caracteristică doar în 2015.
Vezi si
- C (limbaj de programare)
- C ++
- Compararea limbajelor de programare
- Comparație cu COM, GObject, SOM, Windows Runtime, XPCOM
- Swift (limbaj de programare)
- Xcode
- WinObjC (aka: Windows Bridge pentru iOS)
Referințe
Lecturi suplimentare
- Cox, Brad J. (1991). Programare orientată pe obiecte: o abordare evolutivă . Addison Wesley. ISBN 0-201-54834-8.
linkuri externe
- Programare cu Objective-C , de la Apple (2012-12-13)
- Limbajul de programare Objective-C , de la Apple (2011-10-11)
- Ghid de programare Objective-C Runtime , de la Apple (19-10-2009)
- Manual de programare Objective-C GNUstep Base
- Obiectiv-C de Brad Cox
- Întrebări frecvente despre Objective-C