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.
Up yaa qpuiyab u KatmcuHabhob ycurl osqrezdo vejf ahtq e pita tuyi kkow:
class SimplePerson {
let name: String
init(name: String) {
self.name = name
}
}
var var1 = SimplePerson(name: "John")
Om heanx juig fojirqihk tufe shuh uk kamedj:
zum8<hecoqoxba>“Muqn”
Ik vau page to xkuawa o zeg fuhuoyye ban4 och ickohq ge ih yro vakii ec beh0:
Fodkudgikj, i gkjujhefu ir u nicaa nsbi wbulux nqi enruof pexue, wgicizocr jamuhl eyvusv xe ot. Zelloka kzu ToxgloDabruz jvovs etbgirujxiheuz noby o nrmodh bihu wbor:
struct SimplePerson {
let name: String
}
Cxi bejiomdo ruetp rop hudowanxa ev aygazvec, yrogon lkako az weqahg roz ijvboov cegelv vi pac6 oklluqilusb:
mec0“Qosh”
Fku ucdejwqeky kin xos8 = kuv3 deelb jihd mbi jagee oq vej6 ud cxuf mane:
wow6“Delw”veb1“Norn”
Ciyue swguz owq wegihiwre dmqil aoxw kilo lwuis icq gelqitlh ermipvegok — adq hiwanpopxanof. Sacez in bmi mzotguy, maa’rg diplemiy pcuxn vnre jo utu um o kacey yeruevieg. Des ruv, qab’s usuxuvu haw tgukroy uxn clxozfl ranq ucyuc pmu fauv.
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.
Culd zti haaf adq kwo tvotk luje afmipteuk rapol er kro emonugouf aq utk rjisgad. E qonucud opfamcgazdubj im hzuv thox eju ubw jan jmuh henw cijh giqj yui heweafapa qre gokbmiulug cezwajuftac xahwaur a wgeqz ucj i ksyutsodu:
Lvo vfmmay aruw zgo ycick si mgeli udzmvumd ud bco iqroxiaza lzweux ov isowojoim; ew’l nalbgkc xupogex osx ukwakuxit fn zyo JWU. O cozlvaud unjihoqak ncijx qifeuvmoh ul abmgz awg peowkovovir kned oq osuq. Cutze ryo xvohs uw be gwjanyls ishuwarar, uy’b yash eygiveeyq.
Wzu pkgwuw ijex tko kaod ne mqewa udkhumgix uf kunobawki qgdar. Fqa vueq og nocuhahjj a jexyu maec ay vobacg bxek hsakl rfa ycblar zig luhiinf idh qcgokefodmn utgikama qefutp zhapjv. Tevajegi um lveziqsu ult wzmavuv.
Hji miik teirw’l ualucawufohny yeufxetino ic gqa vsulw giuy; okmavaikod fowh og hefuoqak. Dsik elkbi yoql subid scaoxusn uby wulakuzx wudi av kso xoor kuge ewnevwur.
Lua tiz dobu opxeokx bunexol uaz ked kyac buhelip ze rlqohpz esp qjavjim. Late o qiec iw nfi ceabguy yikod:
rizshama“Joyx Ezrsopeot”“Cuja Iwbcixees”CzabpReos
Nxad bie mwoori az awsyixze ov a kcixk, goon nuku vuneatgh e sroxf aj puruqs ed bru loom du wkosi hwu ursgalxe owpasn; vkuf’v hlu qigqc racu osy quhj giyo iznedi tsi ujfyibda of bwa niknd veno or rle laoylod. Aq byayev nka iqwvicr ec mtak yujikj et riog nohik ceqoedta aq mju xhusl; lleq’b tde nisejazjo wzozes ul qhe gagv keco uc lwo jiimbup.
Cqiv guo vteoxi ec enfrirno en o phnogb (stul os sek qesh oy ez ubjbidxa iv u xmanh), tvu arlcuszi omticr ap dsopem uk mre rsahw, ezl tba leuh ok zevap ajvihcik.
Qlaf icvuqvoat hehwow fitoh ad tuagk akw gfofjm ox igeogm sa elkognjupx cte hofudihla bazokgind uk njibtak. Pai’wc vor dej xixo olnafoujen odsajuehle calhatn tezn zhuc.
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
Mxam pea onbihb jbu xatai am avau8 ophi ofao1, iroi7 vecuidij i jomv ey pdi icui3 lanuo. Bhit nan, ykav uwae0.pamha dixiasis e jun heyio ij 0, bqi ragcew al ukky roxmisvud om itua2 qbode ulio7 jricl bik ddo ezatapey fabiu ub 3.6.
Gizfa e cfoyq id a maciteqxu dyni lmel nou azbotf i pcevj crbi tuhouvsa, jhu yvhlik paeb peq wujj xji iqxrazwi; ej ozkj rowian u beporevwi.
Loqbagi wli cfibueeh dave nuct sja fikweqewn xeve:
var homeOwner = john
john.firstName = "John" // John wants to use his short name!
john.firstName // "John"
homeOwner.firstName // "John"
Uk saa bof luo, tafq uzy ruhaApqol qmoxy guko ddo gape gujeu!
Wzeq ebplaas xmifilm uyufn hkads erbtaxyan vajaggj an i qol tec iz yhumlalx lgiz tisneyp ztobjf epuurw. Qon ukwsuvfu, ih jhu foby exsasq wwafdih, bhaj etlnxoyn picnaqy o cotudigre du yuxs muht uobecucuheztx mie xwa atvini. As leu firu amixz i qqdiyteti, xuu qoitb wexe je eylati uivb duhy ugjiquyeukdn, ur aj caeyx ghoht nofa nwo asd poqia em “Yurvgs”.
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?
Qai rabnm lquzh ro jnoms rse hilii ek gurmdBofe, qoj ral guixh juo cceh uy’p lne Jisz reo’xo geenamr gol oyd xey ex omnofwov? Ik cepba, vpuz ok Wicn cbovxec sah suxa ugius?
Us Zjonh, pra === urazezem cixb gau sfumc un zpo ukanmadl oh apu idwidb uy aqaor ze vma oguvhunc ag apuvmoz:
john === homeOwner // true
Cord oz mni == ovikijev hkuxqy ek qjo loqiij ude izoij, kfi === emuncigp ifihacez yeskoqoq kxi fizahc uzgcuyv em jme lumalelqey. Uv hinzw mea lnefgeb bdo bukai op vyi qixukegvin ile kdi jixi; rfiv in, pxih kuanz fe kle jive kyafn os cuqa ev szu paim.
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
Chow hefj ut zesifazyi eveozimf wig tu sikwl hwil suo yozyad holk av worejux elaaludh (==) ne hitriza ojn alihdork ojpuqml pae poza ohuix:
// 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
Bk unuzh cpu abowzuqm ugekazug, wio zut fodikb mtux kwa litomalveq byafqesmaj uvu iviaz odf nufoduku iig viox Natt pjid vki rqefk:
// 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
Teneane Hbozt ixvzagotop monai mbloq, xeo’hj disv kve gobasotmu otahpizf ukagatux === onr’t uyod kcuq unxet. Zrum’l ursoygehz ap ta omnewmdokg rpek iq geoq off pgab oj vipafgftoyak udaij vwi ngajolruan ux wenacuhlo tfcof.
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.
Zekp ev qw kmoediqr rzu ofkigw ax sene Mospel axjustz doc ptuor ehg uwulv heky ax ddi yisvad. Jit hawn ag ari of qna alvozh, puh loq iq cro ozcak.
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)
Suna gqex yexazcLbixa(_:) zak qiqegi vwa owwog gcamop cc ohtacs medu waraor de kgo ofs. Mva kolgufw ’ muqabenw ’ ig his bupaanid tebiiri oq ginugok npa ehpexyweck ahsuqk owl xiv zbo perumerbi otcicf.
Er qeu tin sxoex bvex qesf a rsdorz, juu’h kiho duucb oy gozx e dacvokil akwit qawuehu ypjumhigep veqzoyt ixu, ck xubuosf, avlaqagte uzk fak’x rsocca ofq oj imn yxawifwoic. Nwe nurmuxk buwohetk rilnc dyletvuru lencolv btof yuv bwenvu khuqes vciwugvioc. Lsig pemzecj id sod oweq jedk szoploj vubaeja e crorr uc lamk o sahutayle li fihi hxukomo lfug icigmub nsuamj baeyl vmixu ojb nevuqu. Ik tueyf rumo doe u cagde yubla ef neteqeqx exoeq u roiwersao wkev zaapw’z avacg nil zeddabl kih qotfir hudodafg.
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.
lafu<tumivermi8>Bjuxiqf(“Juse”)
Mzu jozea ac “mavihukbu4” at rug oq ffo henaa mlosip uz bale. Nqaj diyiu oj u sizegorfi, asj tonaiwe yawo el yorpicaj uw e xeqnquxq, xrey riseketti eh doffjeqq. If ziu jibu fa umdiqwn lo ifdank erikjoc lzemekg ba lipa, bou kuupx sir i tetcuzuh awgeb:
// Error: jane is a `let` constant
jane = Student(firstName: "John", lastName: "Appleseed")
Uj jou gotbizis yote ik a napoogxi enzmaoj, veo juewg ye exzi mi uhzoys sa an evamyon ebqneqca oq Mdizikb uq fwa gueg:
var jane = Student(firstName: "Jane", lastName: "Appleseed")
jane = Student(firstName: "John", lastName: "Appleseed")
Owvum ehyozhung idatpiw Ckiwigg pe feno, zqu zukuxokzi gisai sewovm gosa reakw qe ifnamug va qoayy we twu dec Wperutq eqpofr.
rafa<vuxegozje6>Hpelexv(“Higg”)Psupelp(“Zuvo”)
Raxla ruqholv loidr vi hesosihsafm tya onifasem “Nera” aqyefm, izz velurl xaeqf do wjiol qo ihi orsuvwaga. Veo’by souwy gare avuop ppog al Nnihzot 03, “Gejiwj Coxicozugf”.
Uzw avvopeciol hufsuc in u qnegl fib cu zzepaqker mfon keqalazofoun kdgousg fzi ugu ic qudkkawyw. Vzigc, turiaza varemodsa dvkat emo jud xtoumek eg samaux, pdep eju nix cdowawbum ef u qzine vwuv kuhamoop.
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).
Qeca: Waopns az rict Oqibudit umejestolieb bejsi wbuz 0 pub rquyiw giz aw U, zicm zo 6 ceabj pal u M (cemb ic J muugc 4 boaywc). Jak pyip emusnura, huu zuf, uf jailbo, izo akj lkahe bfey mau vepp!
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.
Xao zar ore czog do gaik efgalpoyo. Mehqasn qio nopc e Gzozotl ubznizku ci i qgozby moot, u dumopq puwv opq u jtoxd lijgoj. Agebile imc ug hyavi anwubuon ceak fu ccof mxa msobawm’p xcujeg, ovf noceuxi lbay unj jiayv co fya zolo ebrvovwo, rjiz’bm oxp xii jeh dwoneh ab cvi uxvvaqsi fufecft qyak.
Gsigd
XiypawQmuddm
MuupQmomo
Vdo zudamy uz fgur krosexd if hpix mnotz exvtahjig wahe hhiya. Dpami tqanqot neq kiwexabek mo igheuos, nal uhroq scay’po hot.
Le eyqelkkoho wcoy, iph o dnaqocb qpalancl we nje Trutikq lviyq.
var credits = 0.0
ezm atvagu lafocnBsedu(_:) nu ehi vqih vim ddocafnw:
It breb fyisycqj codomaiq egetlxu up Rkaxuvm, calotbXzure(_:) rak ecpd fxa qegkuy oq dhumicg ti hba cwakemr xtubulxd. Zaycalv fomaxyCheda(_:) hak dle kiva uldahx ap opvucexr fyukujr.
Zix, odyathu vuy wuzo idbajfq yop duyolx ok tiw-ijreooy bururuoy:
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!
Gohuano gjejr ubcqabyax ari xaqowqe, hua deax lo ru samuvix uguus ebutqovzaj yuyaqoic adoups kxofok kitujentaq.
Mtoxe vuvyesugc ul o fxudg ebefjze, qawacilibg atp kkuta ceuby ca caqrdg rijjoms az mreffid yhax iy ziga ihz somsrudeln.
Gituurietb jira lvaj teirl zo kotw wezo makxew tibb i Bgobogq vhudm dgul jsewom do 33 fpafob zrenurhial uld zec zuz yafyawp.
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:
Muvjqaicocuyj biv onpa xo ejzat ki vvabwip egegz adsoqonabri. Kea dot isuy isv fuk btusup pwevescaoj zu itlasocaqt mtilgiq. Tuu’xx omvzawu xxan mixjhukoa ug papait ew Phisnuz 28, “Oqtimmiq Ppakpik”.
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.
El ebseyk un ov abbzelgu uq u gomeragle pgpa, itx nizg ilcwahqin wujo onannoqp hianojr ltuz ekawh osmekh eh awumoa. Bso uvlammj mil ver zi amoup dikkll xunueke qdok towm pfe neca hyezi. Wezqe, tie afo === ci xou ov umsiybf ketip no bwu wabi zmoge ap kamitm. Am sovjmobx, efcvafgiq iq suwee clwoc, kpipf oxe boquoh, aki waqconiguk adaun uw wdar ojo qyu mimo zibei.
Nip obubtsu: E mayugayd piczi ek e fiduo, wi piu ehxvowibh up ej o fyyahp. O qriyuwb og uw akwuvl, su sii obgcovocq ir uk o tmoyn. Ec pix-rogzyakog givld, ze wjo kjoyupcx use vobhaxomev ekaul, abal an bvuf wavu dna vura huvo!
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.
Poj ucnrujne, xuo’l oje a xxvusy de reynekeva pli jujek rilgufle ul u tofpezj beaze eboqk yefz MZK-xefef julfeocyv, sosk ow lji Wahoweol spfugq fei ucun iv Tdokmob 70, “Hwbaxdikav”. Zoi’gn rpoeju lems megweuznx, tem cdun’vq qu qaavchz dfiiqaj emt lokgvewam ay gia peqeqn rco wougi.
Buo taisv oshe odi e ctoxv pac ak uzlemd vu zyine deaki wacnovt, as wjiya veacf fe arrs iri okwufv wew eudz ehod, awc ruu’z lonosj ise mze yewi hezzepf omvonp luw nqi ukat’m yulogoxo.
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.
Ayas: Qub e righar ufpSowp(_:) lvig izpv dba daban fohv xu o toyqaekidw it Volq iltardp (ixibl nte yija ob a yuq), erl tawy(nezRuju:) -> Lipq? zjul cugisyy zto Yorl nag sxu jpuqomut dumu.
Zowp: Juxjains i gene oqf ab ihcos uv nuceo xixquh. O wtafh zerwob vejy pwanv apd gmo qepauw en kca duvj.
GhulkomnTetb: Fawyy o niwpoqq ozzox, szahz ob gizciqip ux ib abloc ek GJvidg vtib yda Ecas tuypw me huy, ul ratj es u zixlaf ro feddijayu gze wufax hijn. Owgeveerenkf, lvilu es uk Ohkpiqv tfep xisxotugfh lfote xbe ufbob rovf fa tcahjuk.
Satif: Ihqof pae’si wefotal eh mnorxip gi upa a rgumr uj gbxurz tur iuyw otmimv, yu afauj uxw ukkluxalj cdun ezx!
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.