Swift From Scratch En introduktion till klasser och strukturer

Hittills har vi täckt grunderna i Swift-programmeringsspråket. Om du följde med, borde du nu ha en god förståelse för variabler, konstanter, funktioner och stängningar. Det är dags att använda vad vi har lärt oss och tillämpa den kunskapen på Swifts objektorienterade strukturer.

För att förstå de begrepp som diskuteras i denna handledning är det viktigt att du har en grundläggande förståelse för objektorienterad programmering. Om du inte är bekant med klasser, objekt och metoder rekommenderar jag dig att läsa igenom dessa ämnen innan du fortsätter med den här handledningen.

eBok: Objektorienterad programmering med Swift

1. Introduktion

I den här lektionen ska vi utforska de grundläggande byggstenarna i objektorienterad programmering i Swift, klasser och strukturer. I Swift känner och uppför sig klasser och strukturer på liknande sätt, men det finns ett antal viktiga skillnader som du behöver förstå för att undvika vanliga fallgropar.

I Mål-C är klasser och strukturer väldigt olika. Detta är inte sant för Swift. I Swift kan exempelvis både klasser och strukturer ha egenskaper och metoder. Till skillnad från C-strukturer kan strukturerna i Swift utökas, och de kan också överensstämma med protokoll.

Den uppenbara frågan är: "Vad är skillnaden mellan klasser och strukturer?" Vi återkommer denna fråga senare i denna handledning. Låt oss först utforska vilken klass som ser ut i Swift.

2. Terminologi

Innan vi börjar arbeta med klasser och strukturer vill jag förtydliga några vanliga termer i objektorienterad programmering. Villkoren klasser, objekt, och instanser förvirrar ofta människor som är nya för objektorienterad programmering. Därför är det viktigt att du vet hur Swift använder dessa termer.

Objekt och förekomster

en klass är en blueprint eller mall för en förekomst av den klassen. Termen "objekt" används ofta för att hänvisa till en förekomst av en klass. I Swift är klasser och strukturer dock mycket lika, och det är därför lättare och mindre förvirrande att använda termen "instans" för både klasser och strukturer.

Metoder och funktioner

Tidigare i denna serie arbetade vi med funktioner. I klasser och strukturer hänvisar vi vanligtvis till funktioner som metoder. Metoder är med andra ord funktioner som hör till en viss klass eller struktur. I klasser och strukturer kan du använda båda termerna utbytbart eftersom varje metod är en funktion.

3. Definiera en klass

Låt oss få våra fötter våta och definiera en klass. Avfyra Xcode och skapa en ny lekplats. Ta bort innehållet på lekplatsen och lägg till följande klassdefinition.

klass person 

De klass nyckelordet indikerar att vi definierar en klass som heter Person. Genomförandet av klassen är inslaget i ett par lockiga axlar. Även om Person klassen är inte särskilt användbar i sin nuvarande form, det är en riktig, funktionell Swift-klass.

Egenskaper

Som i de flesta andra objektorienterade programmeringsspråk kan en klass ha egenskaper och metoder. I det uppdaterade exemplet nedan definierar vi tre egenskaper:

  • förnamn, en variabel egenskap av typ Sträng?
  • efternamn, en variabel egenskap av typ Sträng?
  • födelseort: en konstant egenskap av typ Sträng
klass Person var firstName: String? Var LastName: String? låt birthPlace = "Belgium"

Som exemplet illustrerar, definierar egenskaper i en klassdefinition lik att definiera vanliga variabler och konstanter. Vi använder var sökord för att definiera en variabel egenskap och låta nyckelord för att definiera en fast egenskap.

Ovanstående egenskaper är också kända som lagrade egenskaper. Senare i denna serie lär vi oss om beräknade egenskaper. Som namnet antyder är lagrade egenskaper egenskaper som lagras av klassexemplet. De liknar egenskaper i Objective-C.

Det är viktigt att notera att varje lagrad egenskap måste ha ett värde efter initialiseringen eller definieras som en valfri typ. I ovanstående exempel ger vi födelseort egendom ett initialvärde av "Belgien". Detta berättar Swift att födelseplatsfastigheten är av typ Sträng. Senare i den här artikeln tar vi en titt på initieringen mer detaljerat och utforskar hur det knyter samman med initialiserande egenskaper.

Även om vi definierade födelseort egenskap som en konstant, är det möjligt att ändra sitt värde under initialiseringen av a Person exempel. När instansen har initierats, födelseort egendom kan inte längre ändras eftersom vi definierade fastigheten som en fast egendom med låta nyckelord.

metoder

Vi kan lägga till beteende eller funktionalitet i en klass genom funktioner eller metoder. På många programmeringsspråk, metod används istället för fungera i samband med klasser och förekomster. Definiera en metod är nästan identisk med att definiera en funktion. I det följande exemplet definierar vi fullständiga namn() metod i Person klass.

klass Person var firstName: String? Var LastName: String? låt birthPlace = "Belgium" func fullName () -> String var delar: [String] = [] om låt firstName = self.firstName parts + = [firstName] om låt lastName = self.lastName parts + = [ lastName] returnera parts.joined (separator: "")

Metoden fullständiga namn() är indelad i klassdefinitionen. Det accepterar inga parametrar och returnerar a Sträng. Genomförandet av fullständiga namn() Metoden är okomplicerad. Genom valfri bindning, som vi diskuterade tidigare i denna serie, får vi tillgång till de värden som lagras i förnamn och efternamn egenskaper.

Vi lagrar första och efternamn på Person instans i en array och gå med i delarna med ett mellanslag. Anledningen till den här ganska obekväma genomförandet bör vara uppenbart: För- och efternamnet kan vara tomt, varför båda egenskaperna är av typ Sträng?.

instansiering

Vi har definierat en klass med några egenskaper och en metod. Hur skapar vi en instans av Person klass? Om du är bekant med Objective-C, kommer du att älska medlängden av följande kod.

låt john = person ()

Att instansera en instans av en klass är mycket lik att påkalla en funktion. För att skapa en instans följs klassens namn av ett par parenteser och returvärdet tilldelas en konstant eller variabel.

I vårt exempel, konstanten john pekar nu på en instans av Person klass. Betyder det att vi inte kan ändra någon av dess egenskaper Nästa exempel svarar på denna fråga.

john.firstName = "John" john.lastName = "Doe" john.birthPlace = "France"

Vi kan få tillgång till egenskaperna hos en instans med hjälp av punktsyntaxen. I exemplet ställer vi in förnamn till "John", efternamn till "Hind", och födelseort till "Frankrike". Innan vi drar några slutsatser utifrån ovanstående exempel måste vi kontrollera om det finns fel på lekplatsen.

Miljö förnamn och efternamn verkar inte orsaka några problem. Men tilldela "Frankrike" till födelseort egendom resulterar i ett fel. Förklaringen är enkel.

Även om john förklaras som en konstant, det hindrar oss inte från att modifiera Person exempel. Det finns dock ett tillvägagångssätt: Endast variabla egenskaper kan ändras efter initialisering av en instans. Egenskaper som definieras som konstant kan inte modifieras när ett värde har tilldelats.

En konstant egenskap kan modifieras under initialiseringen av en instans. Medan födelseort egendom kan inte ändras en gång a Person Exempel skapas, klassen skulle inte vara mycket användbar om vi bara kunde inställa Person instanser med en födelseplats för "Belgien". Låt oss göra Person klassen lite mer flexibel.

initiering

Initialisering är en fas i livet för en instans av en klass eller struktur. Under initialiseringen förbereder vi förekomsten för användning genom att fylla dess egenskaper med initialvärden. Initialiseringen av en instans kan anpassas genom att implementera en initialiserare, en särskild typ av metod. Låt oss definiera en initialiserare för Person klass.

klass Person var firstName: String? Var LastName: String? låt birthPlace = "Belgium" init () birthPlace = "France" ...

Vi har definierat en initialiserare, men vi stöter på flera problem. Ta en titt på det fel som kompilatorn kastar på oss.

Inte bara är initialiseraren meningslös, kompilatorn varnar oss också om att vi inte kan ändra värdet på födelseort egendom eftersom det redan har ett initialvärde. Vi kan lösa felet genom att ta bort det ursprungliga värdet av födelseort fast egendom.

klass Person var firstName: String? Var LastName: String? låt birthPlace: String init () birthPlace = "France" ...

Observera att initieraren har namnet, i det(), föregås av func nyckelord. I motsats till initialiserare i Objective-C returnerar inte en initierare i Swift förekomsten som initieras.

En annan viktig detalj är hur vi ställer in födelseort egendom med ett initialvärde. Vi ställer in födelseort egendom med hjälp av fastighetsnamnet, men det är också bra att vara mer explicit så här.

init () self.birthPlace = "France"

De själv sökordet hänvisar till förekomsten som initieras. Detta innebär att self.birthPlace hänvisar till födelseort instansens egendom. Vi kan utelämna själv, som i det första exemplet, eftersom det inte finns någon förvirring om vilken egenskap vi refererar till. Detta är dock inte alltid fallet. Låt mig förklara vad jag menar.

parametrar

Initieraren vi definierade är inte särskilt användbar just nu, och det löser inte det problem vi började med: att kunna definiera en persons födelseplats under initialiseringen. 

I många situationer vill du skicka initialvärden till initieraren för att anpassa den instans som du instanserar. Detta är möjligt genom att skapa en anpassad initialiserare som accepterar ett eller flera argument. I följande exempel skapar vi en anpassad initialiserare som accepterar ett argument, födelseort, av typ Sträng.

init (birthPlace: String) self.birthPlace = birthPlace

Två saker är värda att påpeka. För det första måste vi komma åt födelseort egendom genom self.birthPlace för att undvika tvetydighet eftersom det lokala parameterns namn är lika med födelseort. För det andra, trots att vi inte har angett ett externt parameternamn, skapar Swift som standard ett externt parameternamn som är lika med det lokala parameterns namn.

I följande exempel lägger vi om en annan Person exempel genom att åberopa den anpassade initialiseraren som vi just definierat.

låt maxime = Person (birthPlace: "France") print (maxime.birthPlace)

Genom att ge ett värde för födelseort parameter till initialiseraren kan vi tilldela ett anpassat värde till konstanten födelseort egendom under initialisering.

Flera initierare

Precis som i mål-C kan en klass eller struktur ha flera initialisatorer. I följande exempel skapar vi två Person instanser. I första raden använder vi standardinitieraren. I den andra raden använder vi den anpassade initialiseraren som vi definierade tidigare.

låt p1 = Person () låt p2 = Person (födelseplats: "Frankrike")

4. Definiera en struktur

Strukturerna överraskande liknar klasser, men det finns några viktiga skillnader. Låt oss börja med att definiera en grundläggande struktur.

struct Wallet var dollar: Int var cents: Int

Vid första anblicken är den enda skillnaden användningen av struct sökord i stället för klass nyckelord. Exemplet visar oss också ett alternativt tillvägagångssätt för att leverera initialvärden till egenskaper. I stället för att ange ett initialvärde för varje egenskap kan vi ge egenskaper ett initialvärde i initieraren av strukturen. Swift kommer inte att kasta ett fel eftersom det också inspekterar initialiseraren för att bestämma den ursprungliga värdet och typen av varje egenskap.

5. Klasser och strukturer

Du kan börja undra vad skillnaden är mellan klasser och strukturer. Vid första anblicken ser de identisk ut i form och funktion, med undantag för klass och struct nyckelord. Det finns dock ett antal viktiga skillnader.

Arv

Klasser stöder arv, medan strukturer inte gör det. Följande exempel illustrerar detta. Arvsmönstret är oumbärligt i objektorienterad programmering och i Swift är det en viktig skillnad mellan klasser och strukturer.

klass Person var firstName: String? Var LastName: String? låt birthPlace: String init (birthPlace: String) self.birthPlace = birthPlace klass Student: Person var skol: String?  Låt studenten = Student (födelseplats: "Frankrike")

I ovanstående exempel Person klassen är moderens eller superklassen av Studerande klass. Detta innebär att Studerande Klassen ärverver egenskaperna och beteendet hos Person klass. Den sista raden illustrerar detta. Vi initierar en Studerande instans genom att åberopa den anpassade initialiseraren definierad i Person klass.

Kopiering och referens

Följande koncept är förmodligen det viktigaste konceptet i Swift du lär dig idag, skillnaden mellan värde typer och referenstyper. Strukturer är värdetyper, vilket innebär att de överförs av värde. Ett exempel illustrerar detta koncept bäst.

struktur Punkt var x: Int var y: Int init (x: Int, y: Int) self.x = x self.y = y var point1 = Punkt (x: 0, y: 0) var point2 = point1 point1.x = 10 print (point1.x) // 10 print (point2.x) // 0

Vi definierar en struktur, Punkt, att inkapsla data för att lagra en koordinat i ett tvådimensionellt utrymme. Vi instanserar -enhet1 med x lika med 0 och y lika med 0. Vi tilldelar -enhet1 till Point2 och ställa in x koordinat av -enhet1 till 10. Om vi ​​skriver ut x koordinat av båda punkterna upptäcker vi att de inte är lika.

Strukturer överförs av värde, medan klasserna skickas genom referens. Om du planerar att fortsätta arbeta med Swift måste du förstå det tidigare uttalandet. När vi tilldelade -enhet1 till Point2, Swift skapade en kopia av -enhet1 och tilldelade den till Point2. Med andra ord, -enhet1 och Point2 varje punkt till en annan instans av Punkt strukturera.

Låt oss nu upprepa denna övning med Person klass. I följande exempel ställer vi om en Person Exempel, sätt dess egenskaper, tilldela person1 till person2, och uppdatera förnamn egendom av person1. För att se vad som passerar referensmedel för klasser, matar vi värdet av förnamn egenskap av båda Person instanser.

var person1 = Person (birthPlace: "Belgien") person1.firstName = "Jane" person1.lastName = "Gör" var person2 = person1 person1.firstName = "Janine" print (person1.firstName!) // Janine print (person2. förnamn!) // Janine

Exemplet visar att klasser är referenstyper. Detta innebär att person1 och person2 hänvisa till eller hänvisa till detsamma Person exempel. Genom att tilldela person1 till person2, Swift skapar inte en kopia av person1. De person2 variabel poäng till samma Person exempel person1 pekar på. Ändra förnamn egendom av person1 påverkar också förnamn egendom av person2, eftersom de hänvisar till detsamma Person exempel.

Som jag nämnde flera gånger i den här artikeln är klasser och strukturer väldigt lika. Vad som skiljer klasser och strukturer är mycket viktigt. Om ovanstående begrepp inte är klara, uppmanar jag dig att läsa artikeln en gång till för att låta de begrepp vi täckte sjunka in.

Slutsats

I denna del av Swift From Scratch har vi börjat utforska grunderna i objektorienterad programmering i Swift. Klasser och strukturer är de grundläggande byggstenarna i de flesta Swift-projekt, och vi lär oss mer om dem i de följande lektionerna i serien.

I nästa lektion fortsätter vi vår utforskning av klasser och strukturer genom att titta närmare på egenskaper och arv.

Om du vill lära dig hur du använder Swift 3 för att koda i verkliga applikationer, kolla in vår kurs Skapa iOS Apps With Swift 3. Om du är ny i iOS-apputveckling eller letar efter att göra omkopplaren från Objective-C, så här Kursen kommer att komma igång med Swift för apputveckling.