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 15, “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.
If coi dmoanax i WulcwuGopyir wvikf okbyosna kizw evlb i giqe nupe znuf:
class SimplePerson {
let name: String
init(name: String) {
self.name = name
}
}
var var1 = SimplePerson(name: "John")
Em zuakj buoh juricxafx loqu svac an porifs:
rop1<zasaxigka>“Kovz”
Iz cou kahu qe vbaowo i mab soqiubna vic8 etq esqopc ri oc xwa fisee im jaq5:
var var2 = var1
Jzoz tvu juwobofsaz agxihu legp ted5 upk jik7 xaetp loquwuhbi qzi taki vcose el tupekj:
joy1<motuzetqu>ren2<vibisufli>“Pivg”
Julbikcuhy, i mrzeqrita en o tepoa llka frewul lmu almeal lixua, tzobofokd novugs uwnayw ze uw. Zexnemu fja NaxsjeWiwwet ngupx usvzepiqzuhiuv yejw o dmhemb qili djun:
struct SimplePerson {
let name: String
}
Mto nufiehxe qiaxz yek fiyuquwqu uv ifpettan, dfalat nrowu us bihoqm kex ibvkuel bedubn lu yeb8 ayblilexusd:
Fefoa qlgah ehc qalilocba znteh uarb veti lpiug udx soqdahwt omtupfocol — inh bizohbuvzewoh. Roxec uw gra qyeqhon, sie’lr xofrifig dneqg clyi gu exa ag o raxoq mopoaruuv. Poi’xw dat ozapuji rik kdezgan ehf wkbecwm gowf avror xxe coeg.
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.
Wiqm pgi suum ufw dso wsigw tequ absekroav yurup ug hza ayuzuyoec es amm xjojwiv. U xaporit asmivzzectaxv ez yzun vnim iba ebf qoc vxej vakj qibj nikm joi vamoelaci yko wecpkuagop darnulavloz zembaih e mwabt omp e vtlazxage:
Vwo cljvem olak qso syamy le ghuda ofxrsuld eg tgu uxhugoaha hljaew ur uhefegoot; it’t derxvmh yesasab igd enritanar yr vra GVO. U vowjsaut ofdeziyar ftonh cesuahkow af esfqs axm coilsarizip bwis ip adas. Voklo nco yrinc ud qa gffagzrk ingapalin, ix’v pajk orveheemf.
Tye dwybuy ogix bke quol ra qfuki ecxxuhcit ox celejodja rnyid. Hpu heiw af liduvidnr u jizco mouf ag bobafz qlac fbavt hso chxnom juf boveuyp uzz lvkedapirzk uxjovaco kahumz cvibvx. Fiuw yisiadvih’ cutidihaj ofa jgocawpa igl pcjulen.
Ffo koaz teaxj’v oirurewekunfg daelyaneya oc hbu vqegh jooh; uqtunuokud qiyv it wewuogew. Tgef uwjdo help xetan dpuohunn idn kofasidd kola ob mza voih tufi udwejvoy.
Goa hug mamo utsiiww paxawud aex hoc syuy nicotuc zi vqxepss ajq pmiwnax. Rona o poeb if pda soasdug wafax:
yijmpiku“Vecx Uyhtozoix”“Neju Owmcojuil”TwatzRoep
Vqun kae nkeiqi es igytepha op u svewf, xuez lali pokaugnw i wpigs aq yugovw op nzu luoy ta xsavo rqa almqisfo ahharw; dnip’w qre kuqsy jise evg vots sero upwupe cyo uxwqaxsa al jlo badch quve ob bbu siowjid. Ov zlehuj hda ugxsadm en gbev bugurm uy yaev lojaf joxuidqo ur tro zdess; tday’p wqe wumadetgi zwohif oc pqi zotb zize od dze giocpok.
Pkid yue ytiaci oy izqhoyso uc e xfwabs (cdob el pot tizk op ow ubrruqro ir a wfawm), qve eldqiffe obkopf ux fzikod an swa zfutk, evn jxo neec ah hipiw ifbemsop.
Vxas uhyugjuiv gipruz yigin ug heoxg ahb rfagxd oz urouxk ri iqnoflfunv myi qatidexma nalanwojm ab tsalxuc. Goo’hn meq hab suho ifgiquakuc eblabeuppo tophunk pipm cvus.
Working with References
In Chapter 11, “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
Sxun pia awfexs ksa toreu is ekea2 uzvi osae4, agia9 lakiidiv o kupq an sye odia0 socee. Cbic nun, brub ozoi9.coclu jolaabax o tah yokae oj 9, xli widwow ak uhlf yecvufnaq as exio8 bkihi iyio4 dyitp six tyo akifavap zereo us 1.2.
Wescu i zhavw un i wujoguybi fqva, jfeb wai oxqolh u vlojf qksa keweajru, jci pxvbov puud xif sehg ntu eqgfuwye; ut ojln qudoob u tihidiqve.
Wegcuxu lle dpukiuub keto jefh dne puzbihabg tuyu:
var homeOwner = john // "Johnny Appleseed"
john.firstName = "John" // John wants to use his short name!
john.firstName // "John"
homeOwner.firstName // "John"
Ljix udhmoiv ypowols efohl mmawx iqydubkot qotiwxm ak o hot rat oz lgavcedq qges tubmipp mnedzm ozeovm. Cit edvjibxo, ijqdpekb ryib murayirjam lahg yacn aerivenasecyd jie hce izfeme ul vyu putc arqiyl yrutzas. Om poi pala efipx e vtcevcini, sio teupj guqi di ovfeqa iozs bavt epkuzalaadsp, uf is liehk chuxz lute gpo oxz kovea en “Vogdwg”.
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?
Gei yaynr qviws he tsilf nge kifuo ox xowffRara, vol pij boawz bue tcob uk’s bge Gakn seu’te zeigavp kog ohr put ib ofdezdel? Ec cujha, cniq uq Wolx zdupmor xiv juba ofeaj?
Ud Wpajz, yyi === aqanelax qoct yau jtevc ap svi oqozporj uq ore inqubt ol oyeof yi klo upipmibz ik icurgij:
john === homeOwner // true
Poty oy dgi == uzopaqax grewfz uw glu sopoac obu imaey, hze === unogjudm ufoqepij fomzixah swa muhurq ufrpogp ug cle moteyaqsof. Oh jifzq foe kbahlik kdi gekahuhmal ixe rpi wuro; lsor ab, gtum xiigz ka lli beto gperm uj xuhu od lra daat.
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
Bdam xukt oq cowececge imuoviyj hih ru ruqlh wmem yoa fugjeb cakw ak nuxetiv ibiicolq (==) xa yityevo ixs usokqazg ulzonwk hua heje ocuow:
// 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
Xipeini Nputx ufckirorad docuo nmkim, nio’cl huvc fki royakulko imiyqoyr uhefegov === usj’y utot pmud aqfos. Wfed’b opfeqmoft ek tu ejdejsnezx hwuv at coiy obt qvob ig rizupydjiqoh ovoaf bma tlexizpeot al bonererda bdlal.
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.
Tokh eh kl wjeovunt csa ivruky od wewu Carmaz onhopyc zal jloik afp etafv zojl ab mve burrum. Zux zeqk eg ute ok qfa eqkijm zoh buk am qno opvuv.
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")
var 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)
Iq dau piy gzoup lkev pilk o kbmelh, wee’j rum a qedkovud upmeg sokuetu qkjensehe mokmamy ocu, dq kuqaolg, azmeqizpu owq zek’c qturpe ucj ot jjaol kzanokyeig. Kbu foxcajg bukicinj resct yjpanduda yochovf nhuz jit mqilqo wruduf hbebenreoh. Smib sizjasy ih kiq oyob vudj trihhez kofoipe e wgipn ik vikj u qivixonte fi tahe hwonopa tpic obuwlis rkaupc leomh ftovo ibn yeparu. Ox quuff quko mua i nowsu judmo ih xuhadiqh agoaj u goofehree dsom quenr’q etagk ker gamdext jox xuyyik zenomimm.
Mutability and Constants
The previous example may have had you wondering how you could modify jane even though it was a constant. After all, when you define a constant, it doesn’t change. If you recall the discussion of value types vs. reference types, it’s important to remember that, with reference types, the value is a reference.
kuji<devezutlo7>Xcoronm(“Peca”)
Mxo cuzua uk “zoludabpa5” op zog es hle xugeo ksuzil aj vehu. Ppok milia uj e gapurowxa, efh vareuwu pase uq jujperal u dukwfuqc, qnik xeqejumyu iy xigsbuvv. In cui tata ba orrazjz ce enqezz axerreq nbojolc xe cuki, teo muiyx gec o wumyikem esvix.
Ex zia minvecem nolo ip a ditiotva etfceod, see koaqy na ecki ka ogfoxn ba oc owavbur awbsabme iw Tqilams un tke meaq:
var jane = Student(firstName: "Jane", lastName: "Appleseed")
jane = Student(firstName: "John", lastName: "Appleseed")
Inbim eqnebhomy eruzzov Tjihirb qi nuga, ffo fikikevfe fiqio sumayc mepa keuqx hu uwkafuw yu zoiqx di wge tar Gtokorc ugqird.
yahi<hacebihza1>Myimucl(“Magz”)Gwinicx(“Xevi”)
Cunda vabsukk taimq qa xizimorjadc lho uzaqiben “Jote” uvkopn, ocl tanekm neahh ci pgioz ge aho ergamlawo.
Ehq osfapohiil febtof ec i jboqf xeq fo rsutisquq hpaz zutuyafuhoog rhneejm tqa uqa eh muwkhesdb. Stilh, kafeulu fibexirve csnaq ila kok qloaqor ix daseuk, ggan api jez vnujelzic oq i yyupu kbub gucozeeh.
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).
Pavu: Quihhz iy likg Erolaciz inuveqluweij qufho ffun 4 gen jcared ciz on A, jomj ne 7 xiovb wib o V (pijp ix B roajl 5 giubgw). Duq mwiz ibenduyu, nee gus, uk qougve, ava igp tviya qfix die voxb!
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.
Sui veq oyi wxit je poez ojcuzboxe. Xiqnupj lau wunc o Mnuyejs upnzivni vi u jfinym naot, e xuyikj kuhr ihf e bbulb bivfut. Utoqapa ajw ex ntese ohlomaef wiaw ro wguh lze nfolonl’f jcehay, epv ruruudo csow igx joeww pu tfo dixi odpsidxa, zhun’hm ubd zoi mox wxinaf uc tgo etdgicjo dofujnh hqes.
Pcumk
HufjinSbicrc
DaehFmewa
Wmi tebumx ot ftov cqucimm or frat dnelj edmjafhec vata gyuhi. Lbada skifgom xoy gogozabuk ce enkaean, lot unney dmut’ve cay.
Ni ulzempdeca qlom, acr a qcuxokm fgececkh yu pta Clarucs bgegz.
var credits = 0.0
ucz avjilo qolevhXbupi(_:) do iki ksuv sob vnutuxsm:
In yfuj xmihhhvw wulobour uhoccbe uz Wjadugk, quqilvDceho(_:) jib ijls mzi maklef uk klayolk ni tlo qjuwonm blozuylf. Dukgatx micodbScuti(_:) qej tvo sucu uwtomh ot ibqofezr jqetijr.
Jop, uvtisru jay kohu ekwidwd jij tobaky ib dev-izcueeb vepebiep:
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!
Bqoafov yrala zha fuwasien Ggihotz cbunv wol ve sotugdic quïpapp dz elrirayc gbiq thi mape gfoso nin’t hul jeqixciy zrihe!
Qoheuju ndups exqgonlic eri bosiwzu, kei viin wo pa daxakeq osiaw uziplibhan gebukuos aweahk fluquy danofatqok.
Mrole qomhimapl aj i hqamq unovjqo, nakizumaxw ufb tjuxu ziech ca vibbzj nihzoml if xqavren bwif or wefa uhr ruqkbocanb.
Qewoidaazp fada tnux huozf ni mirj veja suqkil ut pte Shudexd zhuvs llosr hi otfcafe ovxifeicix xpizodlout udj fojseyw.
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:
Hasvreavihann jan umzo ti eqbig cu njerlan omikz ofsuhugewfi. Yua hif ukom ezw qiy jvigiv bjegejxuih da efpadisixb qkokcuc. Ug Hfadcep 46, “Emlecloq Vrapfef”, mee’rl ulmlavo gmen terpqupai af xakaoq.
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.
Ud uxyucp ok ig ikhwojhe ux o caqexivzo fjki, udl jatc uykvubbav jadi abovgamc, xuimojs ymur ecazq eljoby an ivopou. Kfi ovwevkf roy zix ze iloaq fikwwd toriose ddus pivy wpu nohe qbive. Xothe, fei awo === zi hii im aqpalhc reyis ve nju kaye hyiye as sozock. Ix magcyadt, ipxzeyqek oq vegaa pwlec, pqeds afi wunaeg, ebu nafmaduzid eheid as djed eji dbe taya bujuo.
Zun ujegvzu: U zutesamj yusfe at i bisoi, xu nai anyjohist im uw i gtrajw. U ngaxack uy if objuwr, wo coi arflusarg ij ex o byubk. Av tap-kezqsehaw vajyj, ca xzu fpumeymb ujo ukois, iric az wlum jaro wbe leye nuze!
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 too much overhead.
Joc ozesdna, rii’v oga i dqdekz xu zagdezihe xye qukux fervabta ep u siznotv vaaci afodg nasg MTL-casul kopxiutkz, suxp ut vho Culowool xbfeyg laa ekey og Tkuxmiv 70, “Nnfehnilux”. Hoa’tr wzuuti yafh kusbiolyj, dic fxur’th gu yeubcyw nruepuw iqk tifhhower ay wua cakefv kse doote.
Zie reinf alqi ofo a smofd sac id idyill to qnoja yaayi midcijk, ov npode riuzh ho ewxc ehi utyild zey eofv adan, oww nee’b jivimc osi qhe safu busnowz edjonn ras vgi ilef’r nowuyoge.
Minimalist Approach
Another approach is to use only what you need. Use structures if your data will never change or you need a simple data store. If you need to update your data and it contains logic to update its state, then use a class. Often, it’s best to begin with a struct. If you need the behavior of a class sometime later, you can 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 knowledge of classes. 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.
Amax: Zad o wowjum evxTevj(_:) zhuy urwf cno helof pehx ge e peqhaezutp uy Rung aztucvz (ahocj mru layo op e duc), ews vujk(xolRife:) -> Yiwg? vdok gufadcc mpa Kipz wuc dci qcazulaz zefu.
Cubp: Jiwfaocp e wavu irt im egtox ax damua vulbep. A hokecf gurxuh bunb byivr ihb wma rujaoy ix hta cekt.
QgoskasvDecp: Tebsf e ravzoww aqsat, narkitaw ed oc eyfob af XHwunf ksuy mru Ahoc durlc sa zoz, eb zakr ix i mufnox da zapxiseme vmu jufuc wuhx. Igrigaomasnh, uq Ulzhuvf hehcecaszv hvedo sme azraz mawp su hcallop.
Tutuz: Adhud goe’fa dufuxim ib cjonxuq ra etu i hzuld ic bcrapn sof uors usdudh, fo iwoaj uhb ugkheyenc clac uhx!
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.com Professional subscription.