Swift from Scratch een inleiding tot klassen en structuren

Tot nu toe hebben we de basis van de Swift-programmeertaal besproken. Als je meegaat, zou je nu een goed begrip moeten hebben van variabelen, constanten, functies en sluitingen. Het is nu tijd om wat we hebben geleerd te gebruiken en die kennis toe te passen op de objectgerichte structuren van Swift.

Om de concepten te begrijpen die in deze zelfstudie worden besproken, is het belangrijk dat u een basiskennis hebt van objectgeoriënteerd programmeren. Als u niet bekend bent met klassen, objecten en methoden, raad ik u aan om eerst deze onderwerpen te lezen voordat u verdergaat met deze zelfstudie.

eBoek: objectgeoriënteerd programmeren met Swift

1. Inleiding

In deze les gaan we de fundamentele bouwstenen van objectgeoriënteerd programmeren in Swift, klassen en structuren verkennen. In Swift voelen en gedragen klassen en structuren zich erg op dezelfde manier, maar er zijn een aantal belangrijke verschillen die je moet begrijpen om algemene valkuilen te voorkomen.

In Objective-C zijn klassen en structuren heel verschillend. Dit is niet waar voor Swift. In Swift kunnen bijvoorbeeld zowel klassen als structuren eigenschappen en methoden hebben. In tegenstelling tot C-structuren kunnen structuren in Swift worden uitgebreid en kunnen ze ook voldoen aan protocollen.

De voor de hand liggende vraag is: "Wat is het verschil tussen klassen en structuren?" We zullen deze vraag later in deze tutorial opnieuw bekijken. Laten we eerst eens kijken hoe een klas eruit ziet in Swift.

2. Terminologie

Voordat we gaan werken met lessen en structuren, wil ik graag een paar veelgebruikte termen in objectgeoriënteerd programmeren verduidelijken. De voorwaarden klassen, voorwerpen, en instanties verwarren mensen vaak nieuw met objectgeoriënteerd programmeren. Daarom is het belangrijk dat u weet hoe Swift deze voorwaarden gebruikt.

Objecten en instanties

EEN klasse is een blauwdruk of sjabloon voor een instantie van die klasse. De term 'object' wordt vaak gebruikt om te verwijzen naar een instantie van een klasse. In Swift lijken klassen en structuren echter erg op elkaar en daarom is het eenvoudiger en minder verwarrend om de term 'instantie' te gebruiken voor zowel klassen als structuren.

Methoden en functies

Eerder in deze reeks werkten we met functies. In de context van klassen en structuren, verwijzen we gewoonlijk naar functies als methoden. Met andere woorden, methoden zijn functies die behoren tot een bepaalde klasse of structuur. In de context van klassen en structuren, kunt u beide termen onderling uitwisselbaar gebruiken, aangezien elke methode een functie is.

3. Een klasse definiëren

Laten we onze voeten nat maken en een klas definiëren. Start Xcode op en maak een nieuwe speeltuin. Verwijder de inhoud van de speeltuin en voeg de volgende klassendefinitie toe.

klaspersoon 

De klasse keyword geeft aan dat we een klasse met de naam definiëren Persoon. De implementatie van de klasse is verpakt in een paar accolades. Hoewel het Persoon klasse is niet erg nuttig in zijn huidige vorm, het is een goede, functionele Swift-klasse.

eigenschappen

Zoals in de meeste andere objectgeoriënteerde programmeertalen, kan een klasse eigenschappen en methoden hebben. In het bijgewerkte onderstaande voorbeeld definiëren we drie eigenschappen:

  • Voornaam, een variabele eigenschap van het type Draad?
  • achternaam, een variabele eigenschap van het type Draad?
  • geboorteplaats: een constante eigenschap van het type Draad
class Person var firstName: String? var lastName: String? let birthPlace = "België"

Zoals het voorbeeld illustreert, is het definiëren van eigenschappen in een klassedefinitie vergelijkbaar met het definiëren van reguliere variabelen en constanten. Wij gebruiken de var sleutelwoord om een ​​variabele eigenschap en de te definiëren laat sleutelwoord om een ​​constante eigenschap te definiëren.

De bovenstaande eigenschappen zijn ook bekend als opgeslagen eigenschappen. Later in deze serie leren we over berekende eigenschappen. Zoals de naam al aangeeft, zijn opgeslagen eigenschappen eigenschappen die worden opgeslagen door de klasse-instantie. Ze lijken op eigenschappen in Objective-C.

Het is belangrijk om te weten dat elke opgeslagen eigenschap na initialisatie een waarde moet hebben of moet worden gedefinieerd als een optioneel type. In het bovenstaande voorbeeld geven we de geboorteplaats eigenschap een initiële waarde van "België". Dit vertelt Swift dat de eigenschap van de geboorteplaats van het type is Draad. Verderop in dit artikel bekijken we de initialisatie in meer detail en onderzoeken hoe deze aansluit bij het initialiseren van eigenschappen.

Ook al hebben we de geboorteplaats eigenschap als constante, is het mogelijk om de waarde ervan te wijzigen tijdens de initialisatie van a Persoon aanleg. Nadat de instantie is geïnitialiseerd, wordt de instantie geïnitialiseerd geboorteplaats eigenschap kan niet langer worden gewijzigd, omdat we de eigenschap hebben gedefinieerd als een constante eigenschap met de laat trefwoord.

methoden

We kunnen gedrag of functionaliteit aan een klasse toevoegen via functies of methoden. In veel programmeertalen, methode wordt gebruikt in plaats van functie in de context van klassen en instanties. Het definiëren van een methode is bijna identiek aan het definiëren van een functie. In het volgende voorbeeld definiëren we de voor-en achternaam() methode in de Persoon klasse.

class Person var firstName: String? var lastName: String? let birthPlace = "België" func fullName () -> String var parts: [String] = [] if let firstName = self.firstName parts + = [firstName] if lastName = self.lastName parts + = [ lastName] return parts.joined (scheidingsteken: "")

De methode voor-en achternaam() is genest in de klassendefinitie. Het accepteert geen parameters en retourneert een Draad. De implementatie van de voor-en achternaam() methode is eenvoudig. Via optionele binding, die we eerder in deze serie hebben besproken, hebben we toegang tot de waarden die zijn opgeslagen in de Voornaam en achternaam eigenschappen.

We slaan de voor- en achternaam op van de Persoon bijvoorbeeld in een array en voeg de delen samen met een spatie. De reden voor deze enigszins ongemakkelijke implementatie ligt voor de hand: de voor- en achternaam mogen leeg zijn, daarom zijn beide eigenschappen van het type Draad?.

instantiëring

We hebben een klasse gedefinieerd met een paar eigenschappen en een methode. Hoe creëren we een instantie van de Persoon klasse? Als u bekend bent met Objective-C, zult u de bondigheid van het volgende fragment geweldig vinden.

laat john = persoon ()

Instantiëren van een instantie van een klasse lijkt veel op het aanroepen van een functie. Om een ​​exemplaar te maken, wordt de naam van de klasse gevolgd door een paar haakjes en wordt de geretourneerde waarde toegewezen aan een constante of variabele.

In ons voorbeeld de constante John wijst nu naar een instantie van de Persoon klasse. Betekent dit dat we geen van zijn eigenschappen kunnen veranderen? Het volgende voorbeeld beantwoordt deze vraag.

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

We hebben toegang tot de eigenschappen van een instantie met behulp van het gemak van de puntsyntaxis. In het voorbeeld stellen we in Voornaam naar "John", achternaam naar "Doe", en geboorteplaats naar "Frankrijk". Voordat we conclusies trekken op basis van het bovenstaande voorbeeld, moeten we controleren op fouten in de speeltuin.

omgeving Voornaam en achternaam lijkt geen problemen te veroorzaken. Maar toewijzen "Frankrijk" naar de geboorteplaats eigenschap resulteert in een fout. De uitleg is eenvoudig.

Hoewel John wordt verklaard als een constante, dat belet ons niet om de Persoon aanleg. Er is echter een waarschuwing: alleen variabele eigenschappen kunnen worden gewijzigd na het initialiseren van een exemplaar. Eigenschappen die zijn gedefinieerd als constante kunnen niet worden gewijzigd zodra een waarde is toegewezen.

Een constante eigenschap kan tijdens de initialisatie van een exemplaar worden gewijzigd. Terwijl de geboorteplaats eigenschap kan niet worden gewijzigd eenmaal a Persoon instantie is gemaakt, de klasse zou niet erg nuttig zijn als we alleen konden converteren Persoon instanties met een geboorteplaats van "België". Laten we het maken Persoon klasse een beetje meer flexibel.

initialisatie

Initialisatie is een fase in de levensduur van een exemplaar van een klasse of structuur. Tijdens de initialisatie bereiden we de instantie voor op gebruik door de eigenschappen van de instantie te vullen met beginwaarden. De initialisatie van een exemplaar kan worden aangepast door een initialisator, een speciaal type methode, te implementeren. Laten we een initializer definiëren voor de Persoon klasse.

class Person var firstName: String? var lastName: String? let birthPlace = "België" init () birthPlace = "Frankrijk" ...

We hebben een initializer gedefinieerd, maar we hebben verschillende problemen. Bekijk de fout die de compiler naar ons werpt.

Niet alleen heeft de initializer geen zin, de compiler waarschuwt ons ook dat we de waarde van de initializer niet kunnen wijzigen geboorteplaats eigenschap, omdat deze al een beginwaarde heeft. We kunnen de fout oplossen door de beginwaarde van de geboorteplaats eigendom.

class Person var firstName: String? var lastName: String? let birthPlace: String init () birthPlace = "France" ...

Merk op dat de naam van de initializer, in het(), wordt niet voorafgegaan door de func trefwoord. In tegenstelling tot initializers in Objective-C, retourneert een initialisatieprogramma in Swift niet het exemplaar dat wordt geïnitialiseerd.

Een ander belangrijk detail is hoe we de geboorteplaats eigenschap met een beginwaarde. We hebben de geboorteplaats eigendom door de eigenschap te gebruiken, maar het is ook prima om meer expliciet te zijn als dit.

init () self.birthPlace = "Frankrijk"

De zelf trefwoord verwijst naar de instantie die wordt geïnitialiseerd. Dit betekent dat self.birthPlace verwijst naar de geboorteplaats eigendom van de instantie. We kunnen weglaten zelf, zoals in het eerste voorbeeld, omdat er geen verwarring bestaat over het eigendom waarnaar we verwijzen. Dit is echter niet altijd het geval. Laat me uitleggen wat ik bedoel.

parameters

De initialisator die we hebben gedefinieerd, is op dit moment niet erg nuttig en lost het probleem waarmee we zijn begonnen niet op: het kunnen definiëren van de geboorteplaats van een persoon tijdens de initialisatie. 

In veel situaties wilt u de beginwaarden doorgeven aan de initialisatie om de instantie aan te passen die u aan het instantiëren bent. Dit is mogelijk door een aangepaste initializer te maken die een of meer argumenten accepteert. In het volgende voorbeeld maken we een aangepaste initialisatie die één argument accepteert, geboorteplaats, van type Draad.

init (birthPlace: String) self.birthPlace = birthPlace

Twee dingen zijn het vermelden waard. Ten eerste moeten we toegang krijgen tot de geboorteplaats eigendom door self.birthPlace om dubbelzinnigheid te vermijden, omdat de lokale parameternaam gelijk is aan geboorteplaats. Ten tweede, ondanks dat we geen externe parameternaam hebben opgegeven, maakt Swift standaard een externe parameternaam die gelijk is aan de lokale parameternaam.

In het volgende voorbeeld maken we een andere Persoon bijvoorbeeld door de aangepaste initializer aan te roepen die we zojuist hebben gedefinieerd.

let maxime = Person (birthPlace: "France") print (maxime.birthPlace)

Door een waarde door te geven voor de geboorteplaats parameter naar de initialisator, kunnen we een aangepaste waarde toewijzen aan de constante geboorteplaats eigenschap tijdens initialisatie.

Meerdere initializers

Net als in Objective-C kan een klasse of structuur meerdere initializers hebben. In het volgende voorbeeld maken we er twee Persoon instances. In de eerste regel gebruiken we de standaard initializer. In de tweede regel gebruiken we de aangepaste initializer die we eerder hebben gedefinieerd.

laat p1 = Persoon () laat p2 = Persoon (geboorteplaats: "Frankrijk")

4. Een structuur definiëren

Structuren lijken verrassend veel op klassen, maar er zijn enkele belangrijke verschillen. Laten we beginnen met het definiëren van een basisstructuur.

struct Wallet var dollars: Int var cents: Int

Op het eerste gezicht is het enige verschil het gebruik van de struct sleutelwoord in plaats van het klasse trefwoord. Het voorbeeld toont ons ook een alternatieve benadering om beginwaarden aan eigenschappen te leveren. In plaats van een beginwaarde in te stellen voor elke eigenschap, kunnen we eigenschappen een beginwaarde geven in de initialisator van de structuur. Swift zal geen fout veroorzaken, omdat het ook de initializer inspecteert om de beginwaarde en het type van elke eigenschap te bepalen.

5. Klassen en structuren

Je zult je misschien beginnen af ​​te vragen wat het verschil is tussen klassen en structuren. Op het eerste gezicht lijken ze qua vorm en functie identiek, met uitzondering van de klasse en struct zoekwoorden. Er zijn echter een aantal belangrijke verschillen.

Erfenis

Klassen ondersteunen overerving, terwijl structuren dat niet zijn. Het volgende voorbeeld illustreert dit. Het inheritance-ontwerppatroon is onmisbaar bij objectgeoriënteerd programmeren en in Swift is het een belangrijk verschil tussen klassen en structuren.

class Person var firstName: String? var lastName: String? let birthPlace: String init (birthPlace: String) self.birthPlace = birthPlace class Student: Person var school: String?  laat student = Student (geboorteplaats: "Frankrijk")

In het bovenstaande voorbeeld, de Persoon class is de bovenliggende of superklasse van de Student klasse. Dit betekent dat de Student klasse erft de eigenschappen en het gedrag van de Persoon klasse. De laatste regel illustreert dit. We initialiseren a Student bijvoorbeeld door de aangepaste initializer aan te roepen die is gedefinieerd in de Persoon klasse.

Kopiëren en refereren

Het volgende concept is waarschijnlijk het belangrijkste concept in Swift dat je vandaag zult leren, het verschil tussen waardetypes en referentietypen. Structuren zijn waardetypen, wat betekent dat ze worden gepasseerd door waarde. Een voorbeeld illustreert dit concept het best.

struct Punt var x: Int var y: Int init (x: Int, y: Int) self.x = x self.y = y var point1 = Point (x: 0, y: 0) var point2 = point1 point1.x = 10 print (point1.x) // 10 print (point2.x) // 0

We definiëren een structuur, Punt, om de gegevens in te kapselen om een ​​coördinaat in een tweedimensionale ruimte op te slaan. We instantiëren point1 met X gelijk aan 0 en Y gelijk aan 0. We wijzen het toe point1 naar punt2 en stel de X coördinaat van point1 naar 10. Als we de uitvoer uitvoeren X coördineren van beide punten, ontdekken we dat ze niet gelijk zijn.

Structuren worden per waarde doorgegeven, terwijl klassen door verwijzing worden doorgegeven. Als u van plan bent om met Swift te blijven werken, moet u de vorige verklaring begrijpen. Toen we het toewisten point1 naar punt2, Swift heeft een kopie gemaakt van point1 en toegewezen aan punt2. Met andere woorden, point1 en punt2 elk punt verwijst naar een ander exemplaar van de Punt structuur.

Laten we deze oefening nu herhalen met de Persoon klasse. In het volgende voorbeeld geven we een a Persoon bijvoorbeeld, stel de eigenschappen in, toewijzen person1 naar Person2, en werk het Voornaam eigendom van person1. Om te zien wat doorgeven betekent voor klassen, geven we de waarde van de Voornaam eigendom van beiden Persoon instanties.

var person1 = Persoon (geboorteplaats: "België") person1.firstName = "Jane" person1.lastName = "Doe" var person2 = person1 person1.firstName = "Janine" print (person1.firstName!) // Janine print (person2. firstName!) // Janine

Het voorbeeld bewijst dat klassen referentietypen zijn. Dit betekent dat person1 en Person2 verwijzen naar of verwijzen naar hetzelfde Persoon aanleg. Door toe te wijzen person1 naar Person2, Swift maakt geen kopie van person1. De Person2 veranderlijk points naar dezelfde Persoon aanleg person1 wijst naar. De. Wijzigen Voornaam eigendom van person1 heeft ook invloed op de Voornaam eigendom van Person2, omdat ze verwijzen naar hetzelfde Persoon aanleg.

Zoals ik in dit artikel al verschillende keren heb genoemd, lijken klassen en structuren sterk op elkaar. Wat klassen en structuren scheidt, is erg belangrijk. Als de bovenstaande concepten niet duidelijk zijn, moedig ik u aan om het artikel nog een keer te lezen om de concepten die we hebben behandeld erin te laten doordringen.

Conclusie

In dit deel van Swift From Scratch zijn we begonnen met het verkennen van de basisprincipes van objectgeoriënteerd programmeren in Swift. Klassen en structuren zijn de fundamentele bouwstenen van de meeste Swift-projecten, en we zullen er meer over leren in de volgende lessen van deze serie.

In de volgende les gaan we verder met het verkennen van klassen en structuren door eigenschappen en overerving nader te bekijken.

Als je wilt leren hoe je Swift 3 kunt gebruiken om real-world apps te coderen, bekijk dan onze cursus Maak iOS-apps met Swift 3. Of je nu nieuw bent bij de ontwikkeling van iOS-apps of op zoek bent naar de overstap van Objective-C, deze Natuurlijk begin je met Swift voor app-ontwikkeling.