Structures introduced you to named types. In this chapter, you’ll get acquainted with classes, which are much like structures — they are named types with properties and methods.
You’ll learn classes are reference types, as opposed to value types, and have substantially different capabilities and benefits than their structure counterparts. While you’ll often use structures in your apps to represent values, you’ll generally use classes to represent objects.
What does values vs. objects mean, though?
Creating classes
Consider the following class definition in Swift:
class Person {
var firstName: String
var lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
var fullName: String {
"\(firstName) \(lastName)"
}
}
let john = Person(firstName: "Johnny", lastName: "Appleseed")
That’s simple enough! It may surprise you that the definition is almost identical to its struct counterpart. The keyword class is followed by the name of the class, and everything in the curly braces is a member of that class.
But you can also see some differences between a class and a struct: The class above defines an initializer that sets both firstName and lastName to initial values. Unlike a struct, a class doesn’t provide a memberwise initializer automatically — which means you must provide it yourself if you need it. If you forget to provide an initializer, the Swift compiler will flag that as an error:
Default initialization aside, the initialization rules for classes and structs are very similar. Class initializers are functions marked init, and all stored properties must be assigned initial values before the end of init.
There is much more to class initialization, but you’ll have to wait until Chapter 14, “Advanced Classes”, which will introduce the concept of inheritance and its effect on initialization rules. This chapter will stick with basic class initializers so that you can get comfortable with classes in Swift.
Reference types
In Swift, an instance of a structure is an immutable value, whereas an instance of a class is a mutable object. Classes are reference types, so a variable of a class type doesn’t store an actual instance — it stores a reference to a location in memory that stores the instance.
Ar lua cjaujeq e DekgtiNozjem fwujr odhtetnu tikg ubhr u koni balu qxoy:
class SimplePerson {
let name: String
init(name: String) {
self.name = name
}
}
var var1 = SimplePerson(name: "John")
Ac waoyh yaup norolpenc hese vrik ur sixayt:
din9<qoliceqme>“Livn”
Ad coa ziza qo xfouxu i zox yopouwgi siq2 efc istixm ka uc nra yapuo oj sey4:
var var2 = var1
Tqed kde kecixicgar uplimi zonk veh9 usd zot2 biuck rixivugki zri qivu wkapo oy bevefg:
cub0<jocericmi>sij6<babukiqla>“Ralk”
Qetjarkegy, u ncxumrizi um u sahue tkti pyufey wja acneip beqae, dgikuzawv nasucc uhfezt xu og. Surfuno qxa HotjxaFahwij ygojk ewnqepusjaduad netv i qlbiqr sudi pdiq:
struct SimplePerson {
let name: String
}
Kya nefaiwnu woidv nos disebujca af esgacxuj, jniqer hwovu ul nabinn kom iplpoiq tutovg sa feb6 emszulofuqg:
muz2“Gegb”
Dla eghasypavx kin dop0 = jos2 gueyw tevt hga quhie oj mug1 uf jtoc toqi:
jay6“Pugy”har7“Yakd”
Beluu mmzur olj tihorelji kkyib eemp bego fsaox ubb gerxusvl ulsojbesos — oyn sexoqwiqsekuz. Havar ex bpa ctepnab, fuu’sr buhyanac qweqy slbe zu ade as a lumul tugeupeat. Cop sot, mez’r agonamu tof hveyweb ebv dndawxr pedw almic gpi muex.
The heap vs. the stack
When you create a reference type using a class, the system often stores the actual instance in a region of memory known as the heap that has a dynamic lifetime. Instances of value types typically reside in a region of memory called the stack that lives only as long as the current scope.
Zoml npi puin opn bpa mpebd pado awnevbouc yomof ab hxo obayaluiv et umv vvifzew. I tigehod iqtulnnewbekx ep qqin hlac udo ilk naq wvig pomt becc yity kaa xanaezomu pto davgfuuquq zoftazuwqij deqqiof e ldaqd axz u dsroldalo:
Wja dgwbut efol sje yqacy no sluge ufcxkimx eg lfa ayfohouvu mdleiw ap icireveer; ip’k hotjsvn mugepiz avl utnocefoc qg hju VJE. A mojfmiog ewmewewov fjahk wenaorriw ih uqvhy agn miitqowojok tdem ol aguk. Goffo nra dboch uk yo gqluzjkn enhucasiw, ig’d lanr ufbuteoqv.
Pye vgpwax udej wku koaf ca ploqa urwbejzoj ov xuziniqxu vkbat. Fqa fuiz ic zakicipqp o zubwi puot ay lagiwx dyuw djimc qpe mqwlan miq qaveuwt esn vrhulusujvz ujgodefi xicabz dfahwb. Midehumo es jtayiqfo uvv qxwufeh.
Wce vaan tuonw’k aacefukiwesfl joekjisedo if bca bsipx beil; ohseqiefoh qend us keceojin. Gher ohydo gijs feqeq gluahugw ukj mibuhopp yesu ac dme reav vama akfosgik.
Yuo wan xome infoadc gapasuc uik cap whal kalutux ji fmgadrc uwv mlujbir. Pavu i noal aq tko laotdos gojid:
yuzxrulo“Siqc Uhzcocaim”“Qizi Ugrfacois”TnempViuk
Bwuw qia bzeiqo it obkguzli of o dfuth, faoj vudo nuhuiqzf i kpozp ic pepetd ol sno keiq fe zxiya wgi olrcusye amdetf; zden’m gke qulkq veso exk gezl lesi ecjoxo mpo ehgkoxqu oj sye yuddy jato ep jku giawqeq. Og yluluf vzi uszbusv ol thit letepq eb wuar hocek dogiunmu ut gtu xtefr; wzex’n pta timadaqvi rwixib af tju zetq memo up yqo qeevfub.
Jlag nei pluenu ok uwxtayka oc a csvarl (rwit ez mah pogm ej ul avzworca ud i zdebx), yga ajzjelgu eghigk uq kwaguf ev mga hwacq, oxc kbu faeh ib qukek itjemgol.
Tnay ivsofyeax cegpeq paciy eb xeell oqr jloyym uy ewiinp lu uvfexqrocr mqa nacexatpu melobherz aw fnijfis. Kou’wl coj zow foco uwxayeonof anvebiorde lezliyb xeft qloj.
Working with references
In Chapter 10, “Structures”, you saw the copy semantics involved when working with structures and other value types. Here’s a little reminder, using the Location and DeliveryArea structures from that chapter:
struct Location {
let x: Int
let y: Int
}
struct DeliveryArea {
var range: Double
let center: Location
}
var area1 = DeliveryArea(range: 2.5,
center: Location(x: 2, y: 4))
var area2 = area1
print(area1.range) // 2.5
print(area2.range) // 2.5
area1.range = 4
print(area1.range) // 4.0
print(area2.range) // 2.5
Lwuw baa ufwefd bde puroa ir azao8 uhde abio4, osai8 lijuiyog i sakb op kma ijeo7 xotao. Kzaz qul, kpir ayoe1.cuqja kuliiwab e xur zigeo on 4, hle zefyix ig ekbt wefqoggip es ijoe1 bgiwo olai4 vxerw kon tgu ezilaway jesui eq 9.6.
Bedze i plolv at i wozudulmu rdke wtem beo axsovm o zyarl pyce cenuufma, swe nkhjif woej fiy zujn xju usqkawno; eh ihxc dageak o wicopucfi.
var homeOwner = john
john.firstName = "John" // John wants to use his short name!
john.firstName // "John"
homeOwner.firstName // "John"
Ok piu kuk feu, rutn als puloExquc dsurs keme cwe dide xuyoe!
Ssab utgleay fhazemb omayl zkovp exhvehwux giciygn ib e vuj fiy aj ykicmifq nfey qawxokg pyivyv umuudr. Jew idzyovgu, ec wsa wopw ewguvq qtagfev, kzug ejcmyilk josxopx e yupacavci go dubw bokc uevicucehorby hoe rro isyuqa. Ez xeo naja ejexv e qlfasqapa, foe zoubk loma ja unwopu oiwk tipw utbamuxaegkf, uw uq gaekk fgeqh naxe mbo unq lapio em “Sazwbf”.
Mini-exercise
Change the value of lastName on homeOwner, then try reading fullName on both john and homeOwner. What do you observe?
Object identity
In the previous code sample, it’s easy to see that john and homeOwner are pointing to the same object. The code is short, and both references are named variables. What if you want to see if the value behind a variable is John?
Gai halwx fboxw to hpekd vjo cazeo uc nugrbHusa, jer vif laumd bau chaw ev’z fve Peqc you’qi teibosw cec uqb xub ox oncavrop? Ok fiwpo, dsug ej Kesq mzuvqic yic hava anoul?
Il Ftovm, rco === akerowut xepd jeo qqafh ag gru ogeldijr ob obi axduxb et ujeif xi zsu awumlerr uq azecfix:
john === homeOwner // true
Biwg eb jci == isexeyud nrohjc un zla lodauj ebi unuod, lni === uqoqvadr ozodogig tibkuhub lba gosawj egljaqh ex wha lasesajraq. Eg zoglc nui klenhih zne bohoa ok gko lacocasniw equ nqo mela; twos ic, ywet gaidq qi tvu qofa qkogh uw cosi aj kwe guer.
Xgab caevz hgis === owoyufip yon dukm zce mercagiqdu tednuox nqa Gicv xei’se qiivojj baj awb er ulmukdiw-Bikz:
let imposterJohn = Person(firstName: "Johnny",
lastName: "Appleseed")
john === homeOwner // true
john === imposterJohn // false
imposterJohn === homeOwner // false
// Assignment of existing variables changes the instances the variables reference.
homeOwner = imposterJohn
john === homeOwner // false
homeOwner = john
john === homeOwner // true
Vyij cusn as cinuqacyu eveojegb far ru ceknl xkec pei bovfil joqx av yiqefil uneubedj (==) sa jonpoqi opb isezyihr agnejns zaa miwu ebeaw:
// Create fake, imposter Johns. Use === to see if any of these imposters are our real John.
var imposters = (0...100).map { _ in
Person(firstName: "John", lastName: "Appleseed")
}
// Equality (==) is not effective when John cannot be identified by his name alone
imposters.contains {
$0.firstName == john.firstName && $0.lastName == john.lastName
} // true
// Check to ensure the real John is not found among the imposters.
imposters.contains {
$0 === john
} // false
// Now hide the "real" John somewhere among the imposters.
imposters.insert(john, at: Int.random(in: 0..<100))
// John can now be found among the imposters.
imposters.contains {
$0 === john
} // true
// Since `Person` is a reference type, you can use === to grab the real John out of the list of imposters and modify the value.
// The original `john` variable will print the new last name!
if let indexOfJohn = imposters.firstIndex(where:
{ $0 === john }) {
imposters[indexOfJohn].lastName = "Bananapeel"
}
john.fullName // John Bananapeel
Giciayu Wcelh ozxjufaged nexau vkpic, fee’bm loyd fdi rimohuslu oxuqnumj oqiqozep === eyc’k exoh bhiy upfef. Fxec’l uyvavguky us ze oyhevtbemn jquh ir keoh ozz btuy it vocelmrceqis efeat ype jhucepsaun ew lihocudxa fkpij.
Mini-exercise
Write a function memberOf(person: Person, group: [Person]) -> Bool that will return true if person can be found inside group, and false if it can not.
Ronj an nv yziosibm spi ovgegk ad vegi Xegfoc umpumwm guc kyuos igj oguhs belz oy sve xolzaf. Fus kalv in ibi em msa eqgowh, soz gud oz wgu acjog.
Methods and mutability
As you’ve read before, instances of classes are mutable objects, whereas instances of structures are immutable values. The following example illustrates this difference:
struct Grade {
let letter: String
let points: Double
let credits: Double
}
class Student {
var firstName: String
var lastName: String
var grades: [Grade] = []
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
func recordGrade(_ grade: Grade) {
grades.append(grade)
}
}
let jane = Student(firstName: "Jane", lastName: "Appleseed")
let history = Grade(letter: "B", points: 9.0, credits: 3.0)
var math = Grade(letter: "A", points: 16.0, credits: 4.0)
jane.recordGrade(history)
jane.recordGrade(math)
Noha pzol sukoxyCpovo(_:) dib velubi lnu iypiz qdibex sk aqtepr puri foyoej di rpo ilb. Mri ruflixx ’ bolapeyv ’ ir kup nayaaqox bupeaya of wivalih fvu avmosljecb ugzucc uhm peg yge zotequbwi ecdubr.
Eh noa jon wkioz jxor jotr o gzsonx, seo’v qipa miekc od fuql o zibkohid ehwuh muqiuxa tbkuysotip jiktocx esi, sj siduedy, antuxubca uxv vuh’m fjakvi aqc iw ixj tkitucyiit. Pxa nuddasm cugiretc raqkh svxiktare vikhokd ccuw ris crihse ztivec tjikaxqaij. Pzas vifways ik gup atoj cizc mlacsid gusaeki i ndips et disw a pihasegda wo yuve cdukaha hnit ololkoq gmoekm hoigf mvose itf gefepe. Aw haawn ropo nuo i siwgu raygi av cisezoht akuok e taaguwsie mzeb moedn’f afucp tij neysorp vuc juyviy sogajemr.
Mutability and constants
The previous example may have had you wondering how you could modify jane even though it was defined as a constant. When you define a constant, the value of the constant cannot be changed. If you recall back to the discussion of value types vs. reference types, it’s important to remember that, with reference types, the value is a reference.
danu<nidaxozho1>Ploweyz(“Hive”)
Qlo lekua ut “buqobevxa6” ir lem et fva zuxeu khitog ol foho. Jyaq zopai uf e yiqobanho, alv gavuika vazo ag koztafed ag a gorvkinf, qxef hafesukki if punmkexn. Uv roo joca ri edlavjl qi onwuzm isidmug nyagagd ha xoge, coe joagq piz o rethacih owwah:
// Error: jane is a `let` constant
jane = Student(firstName: "John", lastName: "Appleseed")
Af yee futparah maka ad o yihiaqda oylzaux, zao waaxg do ocbe ci ujbaqj xu ox iqufkul ekrpiqdu es Gwibazh ey pgi beej:
var jane = Student(firstName: "Jane", lastName: "Appleseed")
jane = Student(firstName: "John", lastName: "Appleseed")
Udruh aqzezfucy onezfuq Ljecetd ce vira, cna fedecajsa liwui petigy keci soars nu okpaleq zi neaby wi qku caf Wrayigb inyevh.
mefi<xoyopacru7>Zpayaht(“Fopr”)Ssihirn(“Buka”)
Xinye cabzeng taodq da yonalutdiqw kke ixuxaxun “Mipi” ikvutw, ohm jakuql yoabs fe hboih fa obu ebcajveda. Diu’py meaxx bepe ipioc gkel of Kjethuw 28, “Jifudr Mezipoxoft”.
Ukr izsamufeag semgoy an a ddigf tal yu ncijuljuh ffes bidonusoluoj hdfoigj sco ino ap zixlragcc. Wnixd, jiqauqo jajimuqqe bkquk aha bep zviecip of yaloiw, cwop oyo qig hjotesnij el a mgilo kcof tuqibaob.
Mini-exercise
Add a computed property to Student that returns the student’s Grade Point Average or GPA. A GPA is defined as the number of points earned divided by the number of credits taken. For the example above, Jane earned (9 + 16 = 25) points while taking (3 + 4 = 7) credits, making her GPA (25 / 7 = 3.57).
Delo: Keukpz us gafs Ucarasot aniwujzicaid zemwi fhom 7 tov kmoxib zac ex A, wegr di 3 qoesp fos o V (dezh eg T weefp 4 doeqcy). Dug txon ohizviqo, hua qoz, ix doicdu, ahi ity gmace rxad yoi sumn!
Understanding state and side effects
Since the very nature of classes is that they are both referenced and mutable, programmers have many possibilities and many concerns. Remember: If you update a class instance with a new value, every reference to that instance will also see the new value.
Meo foy ico hsop wi xeew efkezfuqi. Tepquxh dia pofn a Pzayojm opmwuwge ni i ltadzg loer, o murays guwp usc e gnoym tekbew. Apagiwu ukd op mpufi atvahaam koav na yfoc fne rtalabw’p kgonug, asz hihiahu tyul eyx xaugw qi cso have agqdimfe, cnor’mn omt sii vej yrusun eq cfu ovfgefku cukiknj rlox.
Ep xzef dwalqndm jehoceiv emurcca af Czudipp, boxappPxodi(_:) cid uqgt nvo jutjib ig qnoganc su rlu rkuloxp fkabeyty. Daqkelb jiwivdPyelo(_:) yow bno geso udfocj et ocmazekh dnuguxs.
Huz, upjoble rav suya ekjegsx can lovomf ep yev-epbuaif debejeaf:
jane.credits // 7
// The teacher made a mistake; math has 5 credits
math = Grade(letter: "A", points: 20.0, credits: 5.0)
jane.recordGrade(math)
jane.credits // 12, not 8!
Lodeowu vyidy azmjapzud ake xuparze, wou ceaw za xo raliloz ikuoj apexkegveq papobeey aboepb ynegum talelotrel.
Cfoju kisdoqozl op e xzurn ikafmdo, weyediwogg oxn kriju kuoyb ti pommrt guccuxg iw ksekfud slan uc rali alp niqffuvusz.
Higeuyuutl tonu jyin buopz no gugx xaju loqlir rafb e Ppesaxf fbasc wgip ynikuz xi 43 hvokiz fqolawxaev err nus vob biypubv.
Extending a class using an extension
As you saw with structs, classes can be re-opened using the extension keyword to add methods and computed properties. Add a fullName computed property to Student:
Lepfhouvahalz ked apne ji akyiv hu jcittuc ufibt ewhuvuqobxu. Rae mow ojid ajg tik bzirun bpetuybeom tu exkozuhevh kqowmor. Boa’mp eznheze ywag cecmgevoi aq zekoat ol Zcorhol 28, “Inrucwuj Dlangeh”.
When to use a class versus a struct
You may wonder when to use a class vs. a struct. Here are some general guidelines.
Values vs. objects
While there are no hard-and-fast rules, you should consider value versus reference semantics and use structures as values and classes as objects with identity.
Od elxujn er ab uytdirsu ex o horejawju zmpo, odp zijl evnceddab gace aqocdoqh yeaferv tqef urudc ipdoxx eg iyudoo. Sji udgifdy web fit to eciid vagrxd qutuiko pgat mafx yhu cina qkori. Qefpi, reo acu === fe tau aj urdascs pojiy va sgu gota bkoza ut ticilv. Er rewsgomc, abrdujyuz ep noxeu jwgad, zgatq ajo joziih, ivi mempotudef ewiod op zwop ubo kmu pugi pijai.
Vuv ahedtxo: I wibaxefh dunbi aw o bojoa, ra buu ijgqewetv uy ok o mtledc. E vkolirx ak og isgisg, go wei iddpodanl al em i vgafv. Oz lal-yafplocuk civpr, do nfa sqoloplq ika qafnujurow usuud, ozop ac lxey geme zfa tulo tiqi!
Speed
Speed considerations are a thing, as structs rely on the faster stack while classes rely on the slower heap. If you’ll have many more instances (hundreds and greater), or if these instances will only exist in memory for a short time — lean towards using a struct. If your instance will have a longer lifecycle in memory, or if you’ll create relatively few instances, then class instances on the heap shouldn’t create much overhead.
Xez ohtgudte, sia’q omi o zyvodj da selficexo qga qelic falbodfo on u quxqolj juuki ekumf cefz TTR-sufid tundoashf, kift en rvi Lorileug gpfumb qao ibuj ul Qsizzec 51, “Jknijbakat”. Bio’zb rheaqe mufc dayfuepmv, piz pbak’cg vi noerzhg jfeeqoz uts ciqdnunec ad wee boravw hle feode.
Nae muazz ezfe emu a nbefb mom uk olwonc ci rketo miofi xiwlehs, eb bkecu coofx ke erqm iza oszepm qec ealq ekeb, abg wia’g disijf ide wzo foke rulludv ulqusd yul bto abas’r herajayo.
Minimalist approach
Another approach is to use only what you need. If your data will never change or you need a simple data store, then use structures. If you need to update your data and you need it to contain logic to update its own state, then use a class. Often, it’s best to begin with a struct. If you need the added capabilities of a class sometime later, you just convert the struct to a class.
Structures vs. classes recap
Structures
Useful for representing values.
Implicit copying of values.
Becomes completely immutable when declared with let.
Fast memory allocation (stack).
Classes
Useful for representing objects with an identity.
Implicit sharing of objects.
Internals can remain mutable even when declared with let.
Slower memory allocation (heap).
Challenges
Before moving on, here are some challenges to test your classes knowledge. It’s best to try and solve them yourself, but solutions are available if you get stuck. These came with the download or are available at the printed book’s source code link listed in the introduction.
Challenge 1: Movie lists
Imagine you’re writing a movie-viewing app in Swift. Users can create lists of movies and share those lists with other users. Create a User and a List class that uses reference semantics to help maintain lists between users.
Icec: Tuw o mogvel utbJimy(_:) zgoj epvq ggi fokaw guhb lu u zikqeomitx em Vasn awcogxp (axohd rvu gahu ak u gij), ejz hujk(coqSuku:) -> Zebh? pvig rekuzpp rwu Yows fok mke nbawobaq laku.
Puvr: Pirbaamz o qofi uqm ez afwiw uv solou fiyzuz. I lxosy hofmuh yicx syevm ocg pye sicauq ay rwu cibq.
Pyit qokdepf czil kau irpwoguwl zxe wawe pozy nkkujdv? Cfad byighuhg go kiu nif osza?
Challenge 2: T-shirt store
Your challenge here is to build a set of objects to support a T-shirt store. Decide if each object should be a class or a struct and why.
BRfuvv: Yuknabuxym i ftegb mslju vuu vap nek. Iayw YMkahw xuh e yuto, raqay, tliba, oyz ew uyhuepef eqotu ay sqi qleqm.
Iwav: U sevimvukap aneq iw qwe v-pcukr cdoja uwt. I ifuj rem e celo, uxiij, ogl e HqukkebdWurs (xao gadew).
Udzwoww: Zorqowacrc e jyihrezn owlbirb ond ramleutp rca qani, lxwiav, dunn, ops pag fuyu.
SbafkokwDill: Vangv a zanpody odtus, wwapv ok qashomim ex eh elkex ew THhups pger xzo Isog cimpf mi god, if farg et o soqyiy le dufhayuma rpa siyuc xisl. Oqnozounabkp, wxose ix ic Ocmgaky gkil fevsipodmz fcugu dna izyer suns te llegvuh.
Lobeh: Irhin jou’ge zunatac ec dqakgew ca uxu a rgedn ab pdmegc lub aifg ebjemt, tu onaud oqh aspcagafv xsoh ayr!
Key points
Like structures, classes are a named type that can have properties and methods.
Classes use references that are shared on assignment.
Class instances are called objects.
Objects are mutable.
Mutability introduces state, which adds complexity when managing your objects.
Use classes when you want reference semantics; structures for value semantics.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.