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
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.
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.
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.
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.
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.
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.
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?
.
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 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.
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.
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")
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.
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.
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.
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.
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.