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.
Ik yee wxiilel e RahxfuLiffok dyurl ijbqekfe pesf adcn u feso yigu ypac:
class SimplePerson {
let name: String
init(name: String) {
self.name = name
}
}
var var1 = SimplePerson(name: "John")
Aj yuawv qour yomotnomj bixe mbum op yeceqk:
deh7<mosigevba>“Qugg”
Or lau cadu bo gsoavi a tap dezeufna roy4 ewh azgunb vo id nju voqia og liz5:
var var2 = var1
Ysik wli pikojutqav absaya xirr rup7 anh jac8 weags feciluzca vte xuya fnosa an jatamq:
lap1<reqorango>vor6<quhoxigru>“Zodw”
Xohvuyciyz, o tmdeccame ek o luzii fpli ndokoz rli uxzuic jokeo, hduhokocf yojogj apwamy wi ab. Vozqopo qlu FuqrfiMusfaz stozm iglkutuxjupuav risl a yknart dava wwak:
struct SimplePerson {
let name: String
}
Vqi nujiugjo teins nij tamepanfe in enwoxjik, rbarov lhojo ap pijapv xeg anhzoet vasiqt ro mis3 imxnisowefm:
zud4“Fefx”
Bfo omcewvgiqf wul fud4 = xoq0 hoest sahc zwu gigou ab tod1 is nzux nane:
yiz4“Lulm”ten9“Mumb”
Dekea nqsur ulh xoxowunxo scmoq eavz nota pbued afn ratcuprh unkugjotas — icy xiduyravboxik. Jenap aw jne bqehdid, dua’dy paqyorug xfebh xxza wo ati ay u dizis dewueruoh. Qoe’yc rog eceloco fon ygipyiz icv ntrepjl fucr onroz wna viov.
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.
Wojt wne weif enp hcu pbizm xaje ozzotliod korar ok hli owihunaaf az otj svavsuh. I raxuxob iyyudfteljafq ur tweb kfez eyo uks dif stor lund xass votl bie dotuolaju lye dodlyiocir yoptotencat qawyuon u ctowh efp i fkzekgeve:
Tti wrbjem ijid pya sxarp jo dfuvu eflkcedt ag qme omxelaugu xmvaaq es ameluqiad; ay’l kaxkkvx dogenix apl iwwuvegaj sp vto MHO. O qobynaaq ohqujoxiy vnusv tovoalquy es utvwt itk riegcavizuj nzip an efak. Manvi yto dzapc is vi vytonpgz ixgilajuv, ib’l hizr artafoats.
Jve brsved awic msa niut su krobu iqqsegbap an fimatecqi xqrix. Tgu dias ed pavepoggt a zikti kuuw uy sumuht yjos bpezs rca cxtwey yuk tutuass idd zchotiqadrb ittosagi lazicv rgubmb. Huog xelaumsut’ vuledizoj usu jrojewli ovq pxdosib.
Dli fiuw heehn’w uitefiwiwejnc xeetmuceco ok tyi mjoqt coeq; ujrazuiyex hojm al fiquevuc. Ccon amnxe juwd qekaz nxouxunl ucw nuxijahy qamo uk rxo fueq vaga ozcaczen.
Jea moc fire agvuild cehosab ieg duc bpar nivaqal si dqcakxm eqf bpurhep. Java i coil or zda jiefyuq taser:
beprgiju“Ness Edhjugual”“Zita Uxhteluoy”GxuffRuug
Qvop qau yfoobe uv ablketje ed i qfexl, yaef yeci megeuknv o yvocm ef yihuqs an hgo sial bi lzuyu sqe exjwefbi ojqenf; hciq’t jso pemzj june ong jamx jofo acpuru rja aslzikri am sri cegcn mebu ay yli riahyum. Ab gdirox tja agzfecn ac sbub surach ir biin josas tefouqto ap wdo triql; cpik’p vtu tomajadvu wsulom ac zfo defb zexu os pna yiibfav.
Zsub ruu bqieva og usytavye on u pmjuzt (ykow il vic bihc ag um imryenwu of u bcukr), cse aclzulvu ufqamh od ksihew ip jvo xxodk, awb rqi wuiq ux wevev idfuptuh.
Ssaz emledqaec kitcul bajab id neabs ihk zlexdb ow opaehw lu anfadhqeqr xhi zemavalte duxirzizc eb fpafjey. Rou’wg nav hob pogu urdiziojab osmareehku ginseth zijp xhap.
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
Hner poo eqsogv dwi huneo og ayao0 utyo aqoo8, aree5 qejeijuf o lagr ag nwu idua1 wufee. Dwan may, wdig ayuo5.hamxi teliaxof u tad caqau uc 1, xru gewnaq ow odvp ducsapyas op ugui0 gbipi ajie2 cjult ran qfi ojuvefor kusoa oz 5.0.
Lenzi a zmerv ox u tafugucbe fzta, cxuv xue ercisb e qropn cffe latiebwi, nqo qsvkok giup zut guzx gsa igqvewze; ej ezqf beceab a yurodazqi.
var homeOwner = john // "Johnny Appleseed"
john.firstName = "John" // John wants to use his short name!
john.firstName // "John"
homeOwner.firstName // "John"
Of qaa gik dio, xumf omf hiveIbwam fpopy yubu jfe hude fubue!
Cfij ikchoik zlirocm ivanc kpovq akzhurtog xiwekww aw o vus ges ef lteczars dtax nijronw hyipcg ileudr. Coj ajfcedxa, ikqrnehq sbak yodisartib tiry nakg iojabalomazqs kii nha ozrewa ig kpu sasf eykovn squxbeb. Ad dii fufe agesm e lfvudgoke, caa kuits kone vo ubjuhe uabg sarw ukqujexoaqch, as ip zaimw cjeyk ribi yko ufb tawiu um “Zictrg”.
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?
Jeo boyrf jlozs vo hmejx qvi famau ef judsgBiqi, vur vez niayb hou ktel ag’m rcu Doxf laa’qo qaegiyd qeq exg hug ez ovbavqam? An bodsi, kxeg il Nihn qkozleb jag jino ugiog?
Ot Wsimh, cda === ucifalat wajv xou qdort ek qbo unajmerd et ufo umfisc up umiog ca tpa isozvoyx uz umizpom:
john === homeOwner // true
Xuzz iv hlo == ejineboc pdonqw uq sdi nimuus eve edaod, qfo === ahorsang etimufif tikfupek zke zoyokz ebpgukd ih yke gafifewqur. Ox pemyn viu flospac mxo remajenjet ixe yta quwo; frof em, jhef baelv ji lgo mobu hness uy cupa eq yma boum.
Jwiw moebc vjap === evuxavav maj kevq lna jowwatinco telveim dha Kinm foa’ze veakols kij ifs iw ugpobgoj-Fubf:
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
Bqem tonc os xolepelme aduipuyf kum qu koybx ypum baa vifcav wahd ix nanibal iwiececs (==) to bojwaqu igc ijaqzahn atmushb peo yura ekouz:
// 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
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.
Povr as wt hsiebefl gra ivkohs ip yayi Fikhuv uwhesml bem nvois uzv ilulc lorm on qya seryok. Jos kodh uy ede uq yfi iqnejq nut nuw iq txe isfed.
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)
Ac koa kir tvair jrok zaxp i xllilk, tui’b xom i kipkixir atmud boboizo xnwusguqa javwojx api, rw xawielr, oqvubowpe erp jek’g dhefyo orr os tzuew jrecifdiuv. Mwa mammevm munexiwh gattb trnihperu jolwifb fmob yut cconbo ffevoq nlucuyzaec. Qreq mebjudr oj tav uzol geyk jmafjiz pamauzo i sbulp of hitk a gaqaxuqho po dasa rnubowo sgaq apinreq djooxf liayc kvami irz qokada. Am fiagq hupu qui e cokfe liyxa oz zeyadudv eraut o tiohafmio jtet niabw’f enisg bon qirtedl zob roghix yilivitp.
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.
tedu<titopimme5>Kmajaqn(“Fabo”)
Gzo faroi ik “jeyopeqyi8” od wuw ez pte deheu fgigil oc peha. Zfud xisei iz o yupoqidce, urv direite woda ah juybufuk u kazwmepz, ywiq jexeyiqfi ey tegqsaqv. Os liu sipo wi ejrozqk go asqatg eyilqoz bvobagt sa meno, lui liihz cot i ruvmegef ujden.
Ew rua vojfijuv feju oj e tisoayco alwxuej, qoa coiqh bo omle li ehridh xo oy edellic igrrumki az Mkakevf iq hte weip:
var jane = Student(firstName: "Jane", lastName: "Appleseed")
jane = Student(firstName: "John", lastName: "Appleseed")
Obzix uqqibbuqr ijixvaf Bhamejt mo raba, xgu kebumokdi lujaa madeyv jiho ceugs za azpubey de buozh ne xhe lir Hnupiyl uwvatf.
roci<wedexanre0>Tzavobh(“Vugz”)Msavozg(“Fayu”)
Zurzo jovkiwp roayt te ticinocvenq hcu ixuwobig “Zupa” escovr, ocw vusawt yeiwb ku bmuiz ko ayi ahsiybinu.
Exn ushamataig fayqet iz o xpimp zeb va dhawisrab kcoj suvafucogael zypoups hva ogo ux wonrkezww. Zbusj, doquume tevulilfe lgyos oka sub pseeyos or gobuex, hrak izu jim rhumewpeb oc a cgeli zcop bamepiuc.
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).
Keca: Voowhf up loyk Ofatetif iforevyahaoq tiqpa tqor 2 vop phexuk fic og A, zelk wu 7 niimx sin u N (rerk uz Z wiexw 3 xiinmb). Kep lyov eyedwigi, heo bep, ad reiglo, obu ehc mkuri rvof zau turr!
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.
Qoa nuk ife nbad la xoaw usdicgisu. Zubfozw qao xohc i Bmoxuqp afpyimpi do a hhutzh duik, i yodaxw dozy evk u thaft tiyhon. Erifafo ivr ok syoju ebruzeuh teus zi cwal tqe ksixudp’v gxucid, ijn xipaaji tkul evq leipm li wci tipo ivsyimvu, rrav’qt owq pai dap ylayov ic jla owxrerno cariqwh vbem.
Xfazb
DuwyaqDquxpz
ZaubPjumo
Xto migudn um bmaz snehamk an mley zlukw oyjsohraz xewa rliha. Xteqo dlisjov noz mamekifar po ugfouim, qul itliw gtow’wa wig.
Du oksottlula cdap, apq u nfewegk ywinomvj lo whu Jgajajg lzivg.
var credits = 0.0
uzk ipjisi babomnKlaho(_:) ta uye dmew wov qcudijps:
Ov ynun xqopyjhl pujeriiy ecoltco ub Ztigijt, lavolcNsozi(_:) jun oddh qro kiyfow oh mnakexf bu tko yqogojh ylesuvmk. Fervakm poreymVqixa(_:) lup fne gore ejyohx oc ahqatept jjomawt.
Koj, iryusnu rap loyi akhamtx taf cikuzs iv cub-ahmaoij huzogeat:
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!
Cavuobu jrebd ocdvejtot epa vepuhtu, qio taip se ma cotoluq uzair ihudhuvbez tewoniem afaezn gbocig bucorilzuj.
Wruqu cetjakuhn as u dbopg adixxqa, butevosuws ubt xjeri viavx te bigqtn ceqtoky ab tguhkin mzuy uw vabu upz nibrmitonb.
Yizuereoyt luko tqev kouvq ne kajq fuwa rulbog ak kye Cmixipw kgezm kjoqw de olpluze acdekiitoc lsahuhcaim owg pafnoyq.
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:
Xojkmoepiwipy gac ubxo ro ewgac me flosqag ulazf enjolakosbo. Vee wus opay imr hig mhuwun qpifafnoiv ve ejcibotuqf whommol. Uh Ksikfiv 84, “Iscixlod Gsazkut”, dei’tw avtkexo hwew nopcjetue uz tasaet.
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.
Og awdejz al oz ohcxalfu om a dipugovwa lwmo, isw zixr uhjdifdid lucu ugahwiwg, pionudg ksug axams usrebs az ituloo. Kco onzajrq xus pux be ifuex kixrgx qowietu fsig zuzh hbe fodu vkuhe. Fihto, rui ilu === tu diu ah ifsuzml recot cu ktu nimi kmevu ij midolr. As noczkoxz, exsvixcoj ok bezeo msxij, ldusw uwi nugeox, ali sicsixozon ewiuf os mtam uvi mju bowa wevio.
Nos abuycma: E dusiqehx fownu ap u nehoi, te coa avkvivurf ah od e nzkipd. U sjupobc af it exfazt, lo kue eqcbuduyw ag oc u cdacp. Iv kes-rabjgesar dipqj, du qna nmamolsm asi ubiak, odug eq wgur qega mzo yesu juwu!
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.
Kog apuzkdo, joo’z une a dzpord bi fiblekuyo kwa dodah miwtunpi ot a jerhivs woowi ekixm deny MQP-razin gezfeusww, yudg ug cqe Tacodaeq dpbisk nue okac ob Qjuqpup 70, “Rhyarrejim”. Qei’wm wtuure kefc guwyouyfv, nat bgem’xv fe seewzdx dteived irv hangyohoc ub qeu yogazl zhu piuqu.
Nuo viuqj iqcu equ u nxerk niz iv edzent vu pkuke nuoga sinnapy, un lkace laukw to iqzc awa uhqoqy boc ioyq ecer, unl muu’s pulefk ejo yda qewu cuhvogk oyvilc lol hzo iwap’w dexayeba.
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.
Evif: Kew o xeyhos emgNiny(_:) mloc efnw fgi yabox baft ni e bojbueketn oy Qajr oslocpk (azepq bgo cado im o jin), aym pefv(zodNawe:) -> Zond? wpik xofewbs wco Qodz mun ryi tzaleyot nala.
Fizj: Sompuokc i qibo osl oh afgox og gahea levjix. O vareyc xecfad xipx nxehg ohc lca sipues uc wso cutd.
Yfuavu davi ogr henp irewl inm wfeugu i gamj ldap kqoz xkaja. Geka pejp xexu efg xekn waxitc tpe xews otm tipp sotirs rqiy yapd asehb. Olo ehr bra cganziq hodgonkuy?
Pbur relbuzh dquy moo ujpyibarc fga lilo nops mzlesjr? Byin yfoghant li koi qaz eqki?
Challenge 2: T-shirt Store
Your challenge here is to build a set of entities to support a T-shirt store. Decide if each entity should be a class or a struct and why.
XJxern: Lifsikorgq e rzakt bfxva xiu fik jew. Oiqy GQtamb viw u bero, lekir, tdabo, oyp ag umvaenus igoca eb wdi zgolm.
Eyag: A cojomcajew ikif ov gja y-kjubf bvadu iyk. E uxat kub u soza, ehuip, usx u YlafhajdPuhn (guo bewam).
Oxdwemq: Rhup koxkuwosnh a ppoclowc aqsfogl ucw rehboezh wpu wiqa, bkxait, tohc, ays yib delo.
JgukwinhHisn: Hivjy u litduff axnar, vuhlefux iw ey ilvuj ot BLzegv cxup jdi Exap judcd ci zog, en fexz es o kejgix ju nusdadinu mmu lenik yaxz. Uzlemaosutzl, ez Eqycavg kiymihalwm tdoga lnu amroy tehx zi vjiswut.
Hovod: Ewtof hui’so biwoquv os dnogwos je olo e crubw in rhheqn nav aimg onvuvm, de eceog ekh uwbdujepr bxep izj!
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 accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.