Cu elementele de bază ale lui C încă proaspete în memoria dvs., este timpul să vă familiarizați cu obiectivul-C. Diferența cheie cu C este că obiectivul-C este un limbaj de programare orientat obiect, în timp ce C este un limbaj de programare procedural. Aceasta înseamnă că mai întâi trebuie să înțelegem obiectele și modul în care se leagă de clase. Alte concepte cheie pe care le vom explora în acest articol sunt mesageria obiectului, încapsularea și moștenirea.
Obiectivul C și Cacao sunt două componente cheie ale platformei iOS. În ciuda faptului că platforma iOS este încă relativ tânără, obiectivul C a fost creat la începutul anilor 1980 la StepStone de Brad Cox și Tom Love. Limbajul a fost creat într-un efort de a combina limbajul de programare robust și agil C cu limba elegantă Smalltalk. Obiectivul C este un superset strict al lui C și, spre deosebire de C, este un limbaj de programare la nivel înalt. Diferența esențială dintre C și Obiectiv-C este că acesta din urmă este un limbaj de programare orientat pe obiecte, în timp ce C este un limbaj de programare procedural.
Cum a ajuns iOS-ul folosind o limbă care a fost dezvoltată în anii 1980? La scurt timp după ce compania NeXT a fost fondată de Steve Jobs, a obținut licența Objective-C de la StepStone. NeXT a creat NeXTSTEP, un set de instrumente pentru interfața cu utilizatorul pentru sistemul de operare NeXT dezvoltat în Obiectiv-C. Chiar dacă NeXTSTEP a oferit un set revoluționar de instrumente, sistemul de operare NeXT a câștigat doar puțină tracțiune pe piață. În 1996, Apple a achiziționat NeXT și NeXTSTEP a devenit Cacao. Acestea din urma au intrat in mainstream cu introducerea OS X in martie 2001 si ulterior cu lansarea sistemului de operare iPhone si iOS.
În programarea procedurală, un program constă din o serie de proceduri sau rutine care sunt executate pentru a ajunge la o anumită stare. Cu toate acestea, în programarea orientată pe obiecte, o colecție de obiecte interacționează și lucrează împreună pentru a finaliza o sarcină. Chiar dacă rezultatul final poate fi identic, metodologia și paradigmele subiacente sunt substanțial diferite. Modularitatea și reutilizarea codului reprezintă două dintre avantajele principale ale limbajelor de programare orientate pe obiecte, așa cum vom vedea în curând.
Dezvoltatorii noi pentru ecosistemele iOS și OS X se confund uneori cu relația dintre Obiectiv-C, Cacao (OS X) și Cocoa Touch (iOS). Ce este Cocoa Touch și cum se referă la obiectivul C? Cacao este interfața de programare nativă a aplicațiilor Apple (API) pentru platformele iOS și OS X. Obiectivul-C este limbajul care conferă putere cacao. În timp ce acest articol se concentrează în primul rând asupra limbajului de programare Obiectiv-C, vom examina mai târziu API-ul Cocoa și Cocoa Touch mai târziu în această serie.
Un alt obstacol pentru dezvoltatorii programatori orientați spre obiect este distincția dintre clase, obiecte și instanțe. O clasă este o distribuție sau un model pentru crearea obiectelor, în timp ce instanțele sunt apariții unice ale unei clase. Un obiect este o structură de date care are o stare și un comportament. În ciuda diferenței subtile dintre obiecte și instanțe, ambii termeni sunt adesea utilizați în mod interschimbabil.
Să aruncăm o privire la un exemplu: prăjitorii de pâine. Înainte de fabricarea unui toaster, inginerii creează un plan, echivalent cu o clasă. Fiecare prăjitor de pâine creat din plan este un exemplu sau un eveniment unic al clasei. Chiar dacă fiecare prăjitor de pâine este creat din același model (clasă), fiecare are propriul său stat (culoarea, numărul de sloturi etc.) și comportamentul.
Starea unei instanțe este stocată și definită de variabilele de instanță sau de atributele obiectului dacă doriți. Acest lucru ne aduce la un alt model cheie de programare orientat obiect: încapsulare. Încapsulare înseamnă că reprezentarea internă a unui obiect este privată și este cunoscută doar obiectului însuși. Acest lucru poate părea o restricție severă la prima vedere. Cu toate acestea, rezultatul este codul modular și slab cuplat.
Să ilustrăm încapsularea cu un alt exemplu. Viteza masinii este masurata de interioarele masinii, dar soferul stie viteza masinii prin uita la vitezometru. Șoferul nu trebuie să știe sau să înțeleagă interiorul mașinii pentru a cunoaște viteza mașinii. În mod similar, conducătorul auto nu trebuie să înțeleagă cum funcționează motoarele pentru a putea conduce mașina. Detaliile privind modul în care o mașină funcționează sunt ascunse de șofer. Starea și comportamentul mașinii sunt ascunse de șofer și sunt accesibil prin interfața mașinii (volanul, pedala de frână, tabloul de bord etc.).
O altă paradigmă puternică a programării orientate pe obiecte este moștenire clasă. Atunci când clasa A este a subclasă din clasa B, clasa A moștenește atributele și comportamentul clasei B. Clasa B este considerată a fi clasa parentală sau superclama a clasei A. Moștenirea promovează de asemenea reutilizarea și modularitatea codului.
Metodele sunt subrutine asociate cu o clasă și definesc comportamentul unei clase și al instanțelor acesteia. Metodele unei clase au acces la intervalele unei instanțe și pot modifica astfel starea instanței. Cu alte cuvinte, starea unei instanțe (adică a variabile de instanta) este controlată de metodele unui exemplu (adică exemplu).
Datorită modelului de încapsulare, variabilele de instanță ale unei instanțe de clasă nu pot fi accesate în mod liber. În schimb, ele sunt accesate prin intermediul getteri și setteri, metode cu singurul scop de a obține și stabili variabile de instanță. Proprietățile sunt o caracteristică a obiectivului C care face ca crearea accesorilor (getters și setters) să fie trivială. În ciuda utilității accesorilor, devine repede dificil să se scrie metode accesoriale pentru fiecare variabilă de instanță. Vom explora proprietățile în detaliu mai târziu în acest articol. Pentru moment, luați în considerare proprietățile ca ambalaje în jurul variabilelor de instanțe care ușurează lucrul cu variabilele de instanță prin intermediul getters și setters.
Să punem cunoștințele noastre în practică prin crearea unui nou proiect Xcode cu care să lucrăm. Creați un nou proiect în Xcode selectând Nou> Proiect ... de la Fişier meniul.
Așa cum am făcut în articolul precedent, selectați Instrumentul pentru linia de comandă șablon de proiect în cerere categorie sub OS X secțiune.
Seteaza numele produsului la Cărți și să dea proiectului un nume de organizație și un identificator al companiei. Pentru acest proiect, este important să setați tipul de proiect la fundație. Motivul din spatele acestei alegeri va deveni clar mai târziu în acest articol.
Spuneți Xcode unde doriți să salvați proiectul și faceți clic pe Crea buton. Este posibil să observați că proiectul pare diferit de proiectul pe care l-am creat pentru a învăța C. Să luăm un moment pentru a vedea care sunt diferențele.
Proiectul conține încă câteva fișiere și foldere decât instrumentul de linie de comandă pe care l-am creat în articolul precedent. Pe lângă main.m și Books.1, există două foldere noi, Susținerea fișierelor și Cadrele, fiecare conținând un element.
Susținerea fișierelor conține un fișier numit Cărți-Prefix.pch. .PCH Extensia de fișiere ne spune că este vorba despre a fișier antet precomplicat. Scopul său va deveni clar mai târziu în această serie.
Cadrele folder conține cadrele pe care proiectul este legat împotriva. Ce este un cadru? Un cadru este un pachet sau un director care conține o bibliotecă care include resursele sale, precum imaginile și fișierele antet. Conceptul de fișier antet va deveni clar în doar un minut. Cadrele dosarul conține în prezent un singur element, Foundation.framework.
În timp ce creați proiectul, setați tipul proiectului la fundație, ceea ce înseamnă că proiectul este legat de cadrul fundației. Cadrul Fundației este un set fundamental de clase Obiectiv-C. Mai târziu, în această serie, vom examina mai atent cadrele Fundației.
Este timpul să vă creați prima clasă. Ori de câte ori creați un fișier nou (Fișier> Nou> Fișier ... ), vi se prezintă o listă de șabloane de fișiere. Alege Cacao de la OS X și selectați Clasa obiectiv-C șablon pentru a crea o nouă clasă Obiectiv-C. Clic Următor → a continua.
Dați clasei noi un nume de Carte
și setați subclasa lui NSObject
. Așa cum am văzut mai devreme, făcând noua clasă o subclasă de NSObject
, noua clasă va moșteni atributele și comportamentul lui NSObject
. Aceasta înseamnă că noul Carte
clasa devine o funcționalitate gratuită.
Clic Următor → pentru a continua și a spune Xcode unde doriți să salvați noua clasă. Asigurați-vă că ați salvat noua clasă undeva în proiectul dvs. Xcode.
Xcode a adăugat două fișiere noi în proiect, Book.h și Book.m. Book.h este fișierul antet al Carte
clasa și expune interfața de clasă așa cum am văzut mai devreme. O interfață de clasă conține proprietățile și metodele clasei și specifică și superclama clasei. Book.m este fișierul de implementare al clasei și definește comportamentul său prin implementarea metodelor declarate în fișierul de antet de clasă.
Deschis Book.h și să exploreze conținutul său. În afară de câteva comentarii din partea de sus, fișierul antet conține doar trei rânduri de cod. Prima linie importă fișierul antet al cadrului Foundation. Acest lucru asigură că Carte
clasa are acces la clasele și protocoalele declarate în cadrul Fundației.
#import
A doua și a treia linie formează o pereche. În Obiectiv-C, fiecare interfață de clasă începe cu @interface
și se termină cu @Sfârșit
, care sunt ambele directive compilatoare, adică comenzi sau instrucțiuni pentru compilator. @interface
directiva este urmată de numele clasei, a colonului și a clasei superclasei-daca este aplicabil. După cum am văzut mai devreme, clasa părinte sau superclasa este clasa din care ea moștenește atribute și comportament.
Cartelă @interface: NSObject @end
NSObject
este clasa rădăcină a majorității clasei Obiectiv-C. Primele două litere, NS, se referă la originile sale, NeXTSTEP, așa cum am văzut mai devreme în acest articol. Prin moștenirea de la NSObject
, clasele se comportă ca și clasele Obiectiv-C și le moștenesc o interfață de bază pentru sistemul de rulare.
Înainte de a face modificări la Carte
să luăm un vârf rapid la Book.m, fișierul de implementare a clasei. În loc să importați cadrul de fundație, fișierul de implementare importează fișierul antet al fișierului Carte
clasă. De ce este necesar? Fișierul de implementare trebuie să știe ce proprietăți și metode sunt declarate în fișierul antet înainte de a putea implementa comportamentul (adică metodele) clasei. Declarația de import este urmată de implementarea clasei, indicată prin @implementation
și @Sfârșit
.
Carte
clasa nu este foarte utilă în implementarea sa actuală. Se îndreaptă spre fișierul antet și adaugă trei proprietăți an
, titlu
, și autor
, și adăugați o metodă numită bookInfo
.
Proprietățile sunt declarate împreună cu @proprietate
și poate fi declarat oriunde în clasă @interface
bloc. @proprietate
cuvântul cheie este urmat de tipul și numele proprietății. Nu uitați asteriscul din fața lui titlu
și autor
proprietăți, deoarece un obiect de cacao este întotdeauna referit ca un pointer.
#importCartea interfeței: NSObject @property int year; @property NSString * title; @property NSString * autor; - (NSString *) bookInfo; @Sfârșit
Declarația metodei se aseamănă ușor cu un prototip de funcție, dar există o serie de diferențe cheie. Declarația metodei începe cu un semn minus care indică faptul că aceasta este o metodă instanță. Metodele de clasă sunt prefixate cu un semn plus. Semnul minus este urmat de tipul de retur al metodei dintre paranteze, o instanță de NSString
, și numele metodei. Declarația metodei se termină cu un punct și virgulă.
Sunt sigur că vă întrebați ce NSString
este și de ce trebuie să fie menționat ca un indicator. NSString
clasa este un membru al cadrului fundației. Declară interfața pentru un obiect care gestionează un șir imuabil. În articolul precedent, am văzut că un șir din C poate fi reprezentat de o serie de caractere și asta este exact ceea ce NSString
clasa face, gestionează o serie de caractere sub capota. Avantajul utilizării NSString
este că face lucrul cu șiruri mult mai ușor.
bookInfo
Acum, că am declarat bookInfo
în fișierul de antet al clasei, este timpul să îl implementăm în fișierul de implementare al clasei. Deschis Book.m și adăugați următorul fragment de cod undeva în @implementation
bloc. Înainte de a rupe punerea în aplicare a bookInfo
în primul rând, trebuie să vorbim mai întâi despre mesageria obiectului.
- (NSString *) bookInfo NSString * bookInfo = [NSString șirWithFormat: @ "% @ a fost scris de% @ și publicat în% i", self.title, self.author, self.year]; carte de întoarcereInfo;
Știm deja că comportamentul unei clase este definit prin metodele sale. Pentru a apela o metodă pe un obiect, se trimite un mesaj către obiect. Inspectați următorul fragment de cod pentru a înțelege acest concept. Să-l rupem de linie. În prima linie, declarăm un șir nou și îi atribuim un șir constant prin împachetarea șirului în ghilimele duble și precedându-l cu un șir @
semn.
NSString * string = @ "Acesta este un șir de caractere."; int length = [lungimea șirului]; NSLog (@ "Lungimea șirului este% i. \ N" lungime);
În al doilea rând, trimitem un mesaj de lungime
la instanța șirului. Cu alte cuvinte, numim metoda lungime
pe instanța șir și metoda returnează un număr întreg. Integerul este alocat lungime
variabilă de tip int
. În ultimul rând, vom contoriza variabila lungime la consola apelând NSLog
așa cum am văzut în articolul precedent.
Trimiterea mesajelor către obiecte este ceva ce veți face foarte mult, deci este important să înțelegeți sintaxa. Chiar dacă sintaxa pare ciudată dacă ești nou la Obiectiv-C, nu e greu de înțeles. Între paranteze pătrate este obiectul din stânga și numele mesajului sau al metodei din dreapta.
[mesaj obiect];
Metodele care acceptă argumentele par diferite, însă sintaxa generală este identică. NSString
clasa, de exemplu, are o altă metodă numită substringFromIndex:
. Colonul de la sfârșitul numelui indică faptul că această metodă acceptă un argument. Apelarea acestei metode pe un șir arată astfel:
NSString * substring = [substring șirFromIndex: 5];
Obiectivul C este cunoscut pentru numele metodelor sale lungi și verbose. Aruncati o privire la urmatorul exemplu, care include un nume de metoda cu mai multe argumente. Trebuie să recunoașteți că numele metodei indică în mod clar ce face metoda. Numele metodei este împărțit în bucăți cu fiecare bucată acceptând un argument. Mesageria obiectului se va scufunda într-adevăr odată ce vom începe să lucrăm cu SDK-ul iOS.
NSString * anotherString = [string stringByPaddingToLength: 5 cu String: @ "some string" startingAtIndex: 2];
Înainte de a trece mai departe, trebuie să revizuim implementarea bookInfo
. Implementarea metodei începe prin repetarea declarației metodei. Semnul de culcare este înlocuit cu o pereche de bretele curbate, care se înfășoară în jurul implementării metodei. Mai întâi declarăm un nou șir, bookInfo
, și să îi atribuim un nou șir, creat cu atributele exemplului de carte (titlu
, autor
, și an
). La sfârșitul bookInfo
, vom returna noul șir, bookInfo
, pentru că așa se așteaptă metoda, un șir ca tip de returnare.
Trei lucruri necesită clarificări. În primul rând, metoda stringWithFormat:
este o metodă de clasă și nu o metodă instanță. Știm acest lucru deoarece metoda este invocată de clasa însăși, NSString
, nu pe o instanță a clasei. Metodele de clasă sunt comune în limbile de programare orientate pe obiecte. În al doilea rând, specificatorul de format pentru un obiect este reprezentat de @
simbol (precedat de semnul procentual). Ambii titlu
și autor
sunt obiecte-siruri de caractere pentru a fi precise. În al treilea rând, de sine
cuvântul cheie se referă întotdeauna la instanța de clasă. În acest caz, de sine
se referă la Carte
exemplu la care se aplică metoda bookInfo
aparține.
Dacă ați lucrat cu alte limbi orientate pe obiecte, accesarea variabilelor de instanțe în Obiectiv-C ar putea fi confuză. Nu avem acces direct la o variabilă de instanță când scriem self.title
. Aceasta nu este altceva decât o scurtătură pentru [titlu propriu]
. Aceasta din urmă înseamnă că folosim metoda getter pentru a cere instanței pentru variabila de exemplu numită titlu
. Același lucru este valabil și pentru setarea unei variabile de instanță. Uitați-vă la următorul fragment de cod. După cum puteți vedea, utilizarea lui self.title
nu este nimic mai mult decât zahăr sintactic.
// Această atribuire ... self.title = @ "The Hobbit"; // ... este echivalent cu ... [auto setTitle: @ "Hobbit"];
id
, zero
, și NUL
id
Înainte de a începe să folosim Carte
clasa, vreau să vorbesc despre câteva cuvinte cheie care confundă oamenii din când în când. Ori de câte ori doriți să stocați un obiect fără a defini explicit tipul de obiect, utilizați id
tipul de date, care este, de asemenea, tipul implicit pentru declarațiile return și argument pentru metodele Obiectiv-C.
Puterea și utilitatea id
tip de date merge mult mai departe, deși. id
tipul de date este o componentă cheie a tastării dinamice a obiectivului C și a legării dinamice. Este important să înțelegeți că id
tipul de tip de date nu deține nicio informație despre obiectul însuși, altul decât faptul că este un obiect.
În Obiectiv-C, fiecare obiect știe de ce clasă aparține (prin isa
variabilă) și aceasta este critică. De ce este asta? Una dintre punctele tari ale obiectivului C este tastarea sa dinamica, ceea ce inseamna ca verificarea de tip este efectuata la runtime in loc de timpul de compilare.
Cu toate acestea, din moment ce id
tipul de date nu îi spune compilatorului nimic despre clasa în care aparține obiectul, obiectul însuși trebuie să furnizeze această informație compilatorului.
Rețineți că este perfect acceptabil să tastați în mod static un obiect în Obiectiv-C prin specificarea explicită a clasei unui obiect în loc să utilizați id
tip de date.
Acest lucru ne aduce la o altă componentă vitală a runtimei Obiectiv-C, obligatorie dinamică. În Obiectiv-C, o diferență importantă între funcții și mesaje este că un mesaj și obiectul de primire nu sunt unite împreună până la execuție.
Ce înseamnă acest lucru și de ce este important acest lucru? Aceasta înseamnă că metoda invocată ca răspuns la un mesaj trimis unui obiect este determinată în timpul executării atunci când atât mesajul, cât și obiectul sunt cunoscute. Aceasta este ceea ce se numește legare dinamică.
zero
și NUL
În Obiectiv-C, cuvântul cheie zero
este definit ca a nul
obiect, adică un id
cu o valoare de 0
. Sub capotă, nu există nicio diferență între zero
, Zero
, și NUL
, și este posibil să trimiteți mesaje fiecăruia dintre ele fără a fi aruncat o excepție.
Convenția este de a utiliza zero
pentru obiecte, Zero
pentru clase și NUL
in caz contrar. Fiți capabil să trimiteți mesaje către zero
, Zero
, și NUL
are beneficii, dar are și dezavantaje. Pentru mai multe informații despre zero
, Zero
, și NUL
, aruncăm o privire la această întrebare despre Overflow de stivă.
Deschis main.m și adăugați o declarație de import pentru a importa fișierul antet al Carte
clasă. În loc să folosim paranteze unghiulare, folosim ghilimele duble pentru a importa fișierul antet al Carte
clasă. Citatele duble sunt utilizate pentru fișierele locale, în timp ce parantezele unghiulare sunt utilizate pentru includerea globală, folosind calea de includere a proiectului.
#import#import "Book.h"
Imediat sub NSLog
apelați, adăugați următorul fragment pentru a crea o instanță a Carte
clasă.
Rezervați * book1 = [[Alocare cărți] init]; book1.title = @ "Hobitul"; book1.author = @ "JRR Tolkien"; book1.year = 1937;
În prima linie, declarăm o variabilă de tip Carte
și inițializați-o. Acesta este un bun exemplu pentru a ilustra apelurile metodelor imbricate. Prima metodă a apelat la Carte
clasa este aloc
. Detaliile acestui apel nu sunt importante. Esența este că memoria este alocpentru obiectul nou și obiectul este creat.
Datorită cuiburilor de apeluri, init
se face apel la noul obiect creat de aloc
metodă. init
metodă initializează noul obiect, configurarea obiectului și pregătirea acestuia pentru utilizare. init
metoda returnează instanța și, în exemplul nostru, o atribuie instanței Book1
variabil.
Următoarele trei linii ar trebui să fie familiare până acum, am stabilit titlul, autorul și anul publicării din noua carte.
Să creați o altă carte și să adăugați ambele cărți la o matrice Obiectiv-C. Crearea celei de-a doua cărți nu este nouă. Singura diferență este că am folosit în mod explicit setatorii din clasă pentru a seta variabilele de instanță ale noii instanțe.
Rezervați * book2 = [[Alocare cărți] init]; [book2 setTitle: @ "Fellowship of the Ring"]; [book2 setAuthor: @ "JRR Tolkien"]; [book2 setYear: 1954]; NSArray * cărți = [[NSArray alocare] initWithObjects: book1, book2, nil];
În ultimul rând, creăm un exemplu de NSArray
, o altă clasă a cadrului Fundației. NSArray
clasa este o matrice care poate stoca o listă ordonată de obiecte. Ca și în cazul instanțelor de carte, am alocat memoria și inițializăm noua matrice.
În loc să sunați init
, totuși, suntem initWithObjects:
. initWithObjects:
este un inițializator desemnat, ceea ce înseamnă că este un analitic init
cu alte clopote și fluiere pentru a facilita inițializarea obiectului.
initWithObjects:
acceptă orice număr de obiecte pe care doriți să le stocați în matrice. Lista de obiecte ar trebui să se încheie întotdeauna cu zero
.
Am menționat deja de mai multe ori că obiectivul C este o suprasetare strictă a lui C și că putem combina liber C și obiectivul C. Să vedem cum funcționează acest lucru. Începem cu o simplă dacă / altceva
pentru a verifica dacă matricea conține obiecte. Prin trimiterea matricei un mesaj de numara
, acesta va returna numărul de obiecte pe care le conține.
Dacă matricea conține obiecte, folosim a pentru
pentru a itera peste obiectele din matrice. În timpul fiecărei iterații, cerem matricea pentru obiect la index eu
și trimiteți obiectul - o instanță a Carte
clasă - un mesaj de bookInfo
. După cum am văzut mai devreme, bookInfo
returnează o instanță de NSString
, pe care le logam la consola.
dacă ([numărul cărților]> 0) pentru (int i = 0; i < [books count]; i++) Book *aBook = [books objectAtIndex:i]; NSLog(@"%@", [aBook bookInfo]);
Sunt sigur că ești puțin copleșit de Obiectiv-C. Asta este normal. Chiar dacă obiectivul C nu este altceva decât un strat subțire deasupra limbii C, se întâmplă destul de mult.
În timp ce obiectivul C este mai mult decât este discutat în acest articol, știți acum elementele de bază și sunteți gata să începeți să lucrați cu SDK-ul iOS. În următorul articol, vom examina SDK-ul iOS și vom explora diversele sale componente.