Välkommen till del två i denna introduktionsserie om Objective-C. Efter att ha spenderat förra veckan genom att granska grunden för det C-språk som Objective-C bygger på, kommer vi övergången till att fokusera på vad som gör Objective-C till ett så bra språk för mjukvaruutveckling. Specifikt kommer vi att diskutera grunden för Objektorienterad Programmering (OOP) och visa hur man skapar en klass och skickar meddelanden till objekt i Objective-C.
Varför har vi Objective-C? Varför inte bara använda det underliggande C-språket? Anledningen till att vi har Objective-C är att ge oss en objektorienterad lekplats för att bygga våra applikationer. OOP är ett programmeringsparadigm som försöker tillåta utvecklare att tänka på programvarutesign när det gäller objekt och attribut istället för variabler och funktioner. Specifikt försöker OOP att erhålla dataabstraktion, inkapsling, modularitet, polymorfism och arv. Ämnet för OOP kan enkelt fylla en bok (eller en tutorial-serie) ensam, så istället introducerar jag dig till de grundläggande principerna som exempel.
Tänk dig att du har en bil. Du kan tänka på din bil som ett objekt. Det finns många andra bilar i världen och du kanske till och med äger mer än en. Din bil har olika egenskaper till det: fabrikat, modell, färg, motor typ, och många fler. I Objektorienterad Programmering, skulle vi kalla abstrakt begreppet en bil en "klass" och den enskilda bilen som du äger ett objekt eller instans (instantiated object) i klassen. När en ny bil tillverkas, instanseras en ny instans av bilklassen (eller skapas) och ges sin egen uppsättning egenskaper.
Fortfarande lite luddig? En annan stor analogi är kakans och kaksklipparen. Klassen är kakskytten och objektet är kakan.
Så, varför tänka i form av föremål? En av de bästa anledningarna är att det här är hur din hjärna naturligt konceptualiserar livet i den verkliga världen, och det finns många fördelar att kunna abstrahera mjukvaruutveckling på liknande sätt.
Klasser (och därmed objekt) består av metoder och attribut. Om du kommer från ett annat programmeringsspråk kan du vara mer kända likställande metoder med funktioner och attribut med variabler. Vi kommer att diskutera var och en i tur och ordning nästa.
Så vi har en "förekomst" av en bil, nu vad gör vi med det? Tja, vi kör det och fyller det bland annat med bensin. Körning och fyllning med bensin gäller endast de bilar vi använder, vilket betyder att när vi fyller upp en bil eller kör en bil, påverkar vi bara en instans, och inte alla bilar i världen. Därför anses uppfyllandet av bilinstansen betraktas som en förekomstmetod. Det är något vi gör för vår förekomst och bara vår förekomst.
Å andra sidan, om vi frågar den ursprungliga bilklassen hur många bilar som är tillgängliga, är det en klassmetod eftersom vi inte längre bara pratar om bilen vi kör runt men alla bilar i allmänhet.
Många av dessa principer blir tydligare med användning, så låt oss titta på en liten bit av syntax.
I Objective-C kallar vi objektmetoder genom att skicka meddelanden. När vi vill veta hur mycket gas som finns i vår bilmodell skickar vi ett meddelande till vår förekomst och meddelandet är den metod vi vill tillämpa. Programmatiskt ser det ut så här:
[mottagarmeddelande];
Fästena indikerar att vi skickar ett meddelande. Den första parametern är vem som ska få det här meddelandet och den andra parametern är vad meddelandet faktiskt är. Slutligen slutar vi med en halvkolon som är vanligt för de flesta programmeringsspråk.
Så, med vårt tidigare exempel i åtanke, så här är det hur vi skulle interagera med vår förekomst av bil för att lägga till gas i tanken;
[dansCar addGas];
Exemplet ovan förutsätter att vi har instanserat en förekomst av bilklassen och heter "dansCar". Vi skickar sedan "addGas" -meddelandet till objektet "dansCar", vilket motsvarar att ringa en funktion. På ett annat språk kan den här raden se ut som:
dansCar.addGas ();
Låt oss säga att vår bilklass har en tank som lagras i procent. Till exempel, om gasbehållaren är vid 50% är den halvfull och om den är 100% betyder den att den är full mot brädan. Om vi nu vill veta hur mycket gas som finns i tanken tar vi inte bara direkt informationen från en egenskap. I stället skulle vi använda en accessor-metod för att komma åt den interna variabeln för oss. På samma sätt, när vi vill fylla tanken, ger vi inte bara tanken en ny procentandel, vi använder en setter för att uppdatera attributet för oss. Denna process är känd som datainkapsling.
Vad vi menar med datainkapsling är att data ingår (så att säga) med metoder som betyder att vi ska komma åt det, vi behöver använda metoder. Några av er som har programmerat på andra språk och inte hört talas om datainkapsling kan undra varför vi gör saker på det här sättet. Svaret är att genom att inkapslera data finns det en fin kudde mellan klassens utvecklare och användaren av en klass. Eftersom klassmetoderna hanterar och behåller attributen inom klassen, kan de lättare upprätthålla dataintegriteten. En annan stor fördel är att när en utvecklare distribuerar sin klass, behöver de som använder det inte oroa sig för klassens internaler alls. En utvecklare kan uppdatera en metod för att göra det snabbare eller effektivare, men den här uppdateringen är transparent för klassens användare eftersom han / hon fortfarande använder samma metod utan att ändra sin kod.
Detta leder oss snyggt till nästa avsnitt vi ska titta på, vilket är hur Objective-C skiljer gränssnitt från implementering.
När du skapar eller arbetar med en enkel klass i Objective-C ser du att den som standard har två filer. En är implementeringsfilen som är en fil som slutar med ett suffix av .m och gränssnittsfilen som är en fil som slutar med ett suffix av .h.
#importera@interface Bil: NSObject // Det här är attribut som går float fillLevel; // Det är här metoderna går - (void) addGas; @slutet
Först importerar vi Cocoa.h vilket är ett standardbibliotek med mycket återanvändbar kod som vi kan använda i vår app.
Därefter förklarar vi att detta är gränssnittet för bilen, men vi lägger också NSObject i den deklarationen. Lägga till ": NSObject" betyder att Car-klassen ärver från NSObject-klassen. Vi talar mer om arv i en framtida handledning.
Vår förekomstvariabel "fillLevel" förklaras därefter, och vi anger att det är av typen "float", så vi kan enkelt representera en procentandel.
Nästa rad förklarar vår "addGas" -metod. "-" indikerar att detta är en förekomstmetod, inte en klassmetod. "(Void)" -delen innebär att metoden inte kommer att returnera någonting tillbaka när den slutför utförandet. Om klassen skulle returnera ett heltal skulle detta ändras till "(int)" och detsamma för vilken annan datatyp som helst. Slutligen slutför vi metoddeklarationen med en semikolon.
#import "Car.h" @implementation Car - (void) addGas // koden går här för att lägga till gas @end
Genomförandet i detta fall innehåller metoden att tillsätta gas till tanken. Vi importerar också Car.h, vilket är gränssnittsfilen. När vår AddGas-metod sitter kan vi lägga till många fler metoder, men dagens omfattning är att helt enkelt få dig att förstå hur klasserna fungerar snarare än att göra en fullvärdig klass.
Nästa gång kommer vi att se mer djupt på metoder och använda variabler med metoder (liksom grunderna för att hantera variabler i Objective-C). Denna handledning var inte för lång eftersom det ofta är lite förvirrande för nya utvecklare om varför vi delar upp klasser i mer än en fil. Om du känner dig förvirrad, vänligen läs ovanstående eller ställ frågor i kommentarfältet nedan. Klasser kommer att vara en konstant återkommande i denna serie och det är viktigt att du förstår hur de fungerar.
Eftersom denna del av serien var ganska teoretisk, så är det inte för mycket du kan göra för att träna. Jag rekommenderar dock denna vecka att du anmäler dig till Apples utvecklarwebbplats eftersom det är en ovärderlig referens. När du har gjort det, ta en snoop runt några av sina nedladdningsbara klasser och ladda ner några enkla. Du behöver inte förstå all kod, titta bara på hur klasserna bildas och separeras över filer.