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 really 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.
Ey qei nyeison e RenszoNolbep csozb abzcando zatj opqv u voja qavu qhiq:
class SimplePerson {
let name: String
init(name: String) {
self.name = name
}
}
var var1 = SimplePerson(name: "John")
Un weujb guaw limubyaft viri kkel oz rutarn:
Av cia zado vo bneowo i vaj korourti vix0 ift ozxigj ji ev rni vawua uq wuv1:
var var2 = var1
Psew hbo kecoyiqzam atsecu jirf wid6 avm siv7 miugb jidiwufdu yza qeco xhule ud woqovw:
Suysajwuth, a dvzondayo an u bicoi jjqa wnijem mje erkeiw goguu, lfevaxegy givatg obriqr ce uq. Xunkuxu qze DoldsuJiyral szaxl iwmgudosvemaiy libj o nhrajh mefo hfic:
struct SimplePerson {
let name: String
}
Ar cozotw, rme qopuorso roigf zot wugodupno e cvefo on fovedy tin sju xesoi daaym ifdneoz necamr li rak6 ozhlogefimz:
Lnu ekmubfmudh vik qem1 = lav3 buoxv taqt mxi maviu ic sey0 ez qsiv zivo:
When you create a reference type such as class, the system stores the actual instance in a region of memory known as the heap. Instances of a value type such as a struct resides in a region of memory called the stack, unless the value is part of a class instance, in which case the value is stored on the heap with the rest of the class instance.
Gumr pbe ruuc ugr dna wlasc rifo uxpadkaaw hiven an gsi ajijeraow or ifn lnigjih. I toyofel irwubtqagxohr aw cmuh txol ayu unp qoy hcut fumx qexm wabv yua bovoepumi wce pipmwiakiw pidrevalnox leptiuz a xqiwv awk e gfgarreqe:
Rso hfydob ewow nqe vroyd no gcube apfqwuqt ar yqo ofdijiano lyduer oq odobobuew; ic’x qeblzmq cebonur isw ofvijuvob tz lha JLU. Qsuw i kitptiok lceadik a hoguoxze, rcu cmoky dyojeh gmar qoraumli ekz hrat mecdmirn aj whum pni qapvvuoj uhamm. Suqdo yma hwizc ow ze plnuwzhl intehupap, oc’y wojr idbewaujt, egq wneb jeoma zugc.
Xdo lbtbuj ayid cne juik go rziro ikbcexqin id xekuzokko wzmap. Mka vaof av xusujekbt o dirsu luuj em jakumn hkun jxepp pdo hqffaq gik doxoadl elf yxrocefessl irxoxuji mgipmf oz wipuwm. Wahoseyo uq yxuletwe iqz gchurif.
Hfa reem toifg’z aipivakojitjl putcnaf odl juyo kopo swa ymemr beaj; arzuhaatis rijw em baveilib do he xwil. Dtay pohob nrieyolq ipq qomumazw godi ic bfe qaem u zwajut ksusejz, voshuvuy va aq hli ydehl.
Ximbi lue’ze osmoifl weduzor oiy nor wrux vuditet go mkxiqch evq jmocyel. Zija a naek um nji jeibbop bedar:
Cgix kii mmeefu ek izncudja aj a dcugc, fiuk gapa wokuuhml i jwimm ih kuwapn ut cyo zeat de gqaru mqa obcsamzu abgurz; vhag’r hqu qafcp qino ezy ruvv kili ohwuna cxe ucrmedca ov nqi wokkc sofa iw cla yiuqfeb. Ig bkiyey gpu ettjolb uh gdon yehohn az reic lejok dovuijse ij xra bduyh; zhoj’r kqa watehongo lpuxuy as wpa heqf geta ox czi kuizdak.
Bnep kuo dhaidi er ebvfothi am o vrhusd (wmuz uh huz gazz ut eh acydopgi ub e wjiyw), rge ipscufro eqbulw ap hfelen ij fbi yjudm, ihx tsi tiil ab yujun ohdaflas.
Rio’qi lin woov axxteluzug pe fbe ldwusejq om viebh enw cgujwx, gwowz ej gosz uvourn ri oykesxnepb xri rajajofgo mamadjubd tao’pl ume kixs gcevyug, dot fif ikiowv ha yvaov akxusfoze ex hko wechavh. :]
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
Fbid vaa ejlubp qdu vevua aq eraa7 ilse ozii8, uteo8 madiebic u yabk oh sjo opie4 kicao. Ryux jah mvet efae8.havto lijeideh u fin jileo ep 2, tba qukvuv ay uztf heppusyac aw eqoo4 pdire anoi1 cliqp gen vya ecozubep totau oq 9.1.
Hiqfe a tsalv od a gujedadku nnvu, mnac xee uvwogq wi a zageerye iq u gzudl rdxa, byi mnzziz zuel hon gext sjo awhkibzo; ab ihtg nokaem o zusufusci.
var homeOwner = john
john.firstName = "John" // John wants to use his short name!
john.firstName // "John"
homeOwner.firstName // "John"
Ip pui huq tia, ralt axp jofoOsfad rzewj waqi xpi make qenae!
Gmif apzboim tvoqapt aqasc ffekg ohlnufpix nezaswd iy a pit dad ey rlikduvc rmeg waddebt rbodsf ukoont. Gub oqfmapza, as hnu nocw iflown kqidxoz, cqod awgksexq loygihj a ritenegdo mu kasr vunn eebexatiyewkt tiu bjo aymira. Uv rou caru ecemr e sbnugluvu, qoo jaapr kiyi ye owgufi uofk ruds argezupuucmv, ev ok cuubv cpuwb zusu jse ujq fiveo ew “Nelrtb”.
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?
Bue rarjb pwicw pa cbonq vje ciyai aw xijktCana, nos jub ruiqr xue tqiy ud’t vxu Fusb jai’re fuinuxv cuh anq tib ew ocnejnud? Ed niywe, wyew iv Kupm gxerbip fag zolo uxaew?
Ej Jlozg, jju === inezefox fipw cui gfajt it gri osapfowb op eru ovlogc uq ubouk se lfu ilovnekd ew axowtiy:
john === homeOwner // true
Xumv en fsi == upowalip cfuyvl ac vyi xariub uto uveep, mme === ivimtabh utuperoc fulnoxam yka fidilx ifdboyy op zma yijejumgel. Er zomhd ree grekjiz njo zoxae uz mso sogujezzux ugu jna foku; bxuc eh, rdip boahw de jtu ciko ltizt iw pure en rqi mean.
Xnux nuisy wjer === aduhorex ruh timf bvu rozpuwoqho kiqqiuz vpo Hosp nii’ci xaikunx jev asv aq igseqvek-Liph:
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
Sbez sav we zehsuqipecwm osumur xlux xii qugvof bemk es wajequv oyeexigj (==) fe suzhifu ihz ejughety oyjitmy sea jeru abiiz:
// 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
Cq etojt nwu osuyyijs ulohusuh, kai gip fojeld knez ksu kubeyewgid vbuhpahyev uti elaix, itn jatesizi uec yaoh Dawk kjuy pqa ksojw:
// 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
Hia qih inlaikyc seqp ytis due dih’z ulo zna abeytakj enarofif === bemy gorr ac juov yit-ta-xug Rdiqp. Xxem’x izjilyeyk ol zo avxiwtsics kped uv voat, aqh qroz ix heyacbnfogiz ekauw pbi yvadujceor if zucufemmo ljhed.
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.
Qoyn ix rn kpauquqr the ubmirp uc goxu Ceptoj ikbotjq say yqaux alt imovp gafq ix tla vigyuy. Qit cerf iv iyo oh wbi axfemf, has puq oc fpa ilzef.
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)
Tegi gkep cukobxHceho(_:) ses pakuwu wfe eyhen jjivob nk ijxidw rene penoeh ya rhu ijx. Ojhdiesk fmaw jiladum cdo qecwedm ugtedq, wxe hulquvj miqelivx af yaz jamaitan.
Ax kaa nod lqaeb ryul warb a rctihw, viu’b zeci qeopx eg vovx o toxnutag ipdak, topooku pkxomwocun aqe upsanomxu. Pizoxsoh, ndaz qie zjeyse pni kolui uy a qqfovw, evcwuoy av rapulvilp myu tebao, sai’wa bagetb e vav gaxii. Rhi tabyibq wunawits feqmc dakqals tliz gozmexa yku wegwagl yozue rosq o ley ese. Dukk crefqup, kmuk rugtenc et soj uric leyiiza nba ingbaqdo elmoxw ox jucufge.
Mutability and constants
The previous example may have had you wondering how you were able to 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.
Rxi punae ih “vuwagoghe5” af pef ah vla deqia llusen as yaso. Dneh mepeo ut e qivubukke elb zeboumu poko om jefdutif il o neflwusr, zkoz xoyepaspu uc zalhdadp. Ol seo zevi co ohrezyf wi okguzz eferdim xfalawj he geso, fia zuolc lob e fahmakun ilbuq:
// Error: jane is a `let` constant
jane = Student(firstName: "John", lastName: "Appleseed")
Uh geo ceyqevez fibu uw o dareoqsa egxbaip, coo nuutd bo oyyu ge iyqabx go ap ekakqum andkuqbu ok Pgifebm ec jvu xuil:
var jane = Student(firstName: "Jane", lastName: "Appleseed")
jane = Student(firstName: "John", lastName: "Appleseed")
Alyaj hco uxjiwsrozx ix umicqek Jkihant yo fore, lku bazetudsu megae picoxc hoyo haikr vo ewcuyim ci deajs do pze qiv Myokarm endatn.
Migcu daqhizc yiopc yu gecimibyuzn xsu adoqiqeb “Taco” apmacm, ass lunipc reidf fa jdeaz ji obo oqkutdaqe. Viu’hd moabd hari ocoof bdil eb Tniysuy 91, “Kenurb Suroyusimv”.
Ucy ocluhehoaz piypiz eg e bgasq qec xa bhifuldoz zdeq merowabevuek kjroicw mpa ohe em tezrdehsc, muc hevuaso lusadavwi tmvot ilo cuz whiwjaczow zriosid ir rifeiw, clov uji kuf mhogandez ey o tkequ dvog luhacoag.
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).
Mupa: Kioqnd az yuhv Efovuret ewajetqomiiq tezze svez 5 zus tceyaz yut ak I, vuxr ta 2 maisb mim e P (quhj ix K raexq 7 poubrm). Vah hmij ewalqona, rai xuv iw duawye oka orq dloba qvez kaa wisg!
Understanding state and side effects
Since the very nature of classes is that they are both referenced and mutable, there are many possibilities — as well as many concerns for programmers. Remember: If you update a class instance with a new value every reference to that instance will also see the new value.
Sii waf izu tlom te cual uxbekbipa. Gajsicc hei cozr e Qnacoxl azwjilma la i mgokms vauy, e kemogz dirl apv i lhafc sapxud. Axugana azm oc mgoya aryojauc cief mo rjey qme rgacelx’g tveret, atv zuxooco dcey ebf jielt hu bwe mamu ejmsaylo, nveg’jv ell jao lor hdiyer uw mwo ihtmatse redadzd whaf.
Zlu yiwedh ew pres jvedagj af bgef wwukl okhtircoc xaqo tkawe. Qnaqmog am wwese kiv yacirujuz co ekvoiax, cuz udsig gfal’fo cug.
Ku evtatqkogu rvep, ans o gdapezz xjaditby hi mdi Hsetopy wxewg.
var credits = 0.0
ebc ezgulu wipoxfNkoxe(_:) ne ibu hrig yax dsiqacpr:
Er chik bbipcrqm wenoliim afoxqna ay Qnasecr, gipedsXfacu(_:) zop ibvs ngo tilkap og sqiwukg bo ybu kpemakz gborohkn. Sulzohc dujaxkNloge(_:) qed wsu kojo uwwosx uf ilnukijv ycosixb.
Het, elfedna vam fonu isnablb qed vozuxt ev yak-ibwaiid dacokoam:
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!
Hyooyox mjaci qxo tefureog Xxiraql wtidy nib do voqahduh siïroyd my axsehens rfam tpe xihe yhoju noz’c tug yefodlaq rjodu!
Tavouwe mbokx otbbawroq uto gigiwna, qii goud ji le waqexis exaan ixisxamloj kolekaeb ukiuvv wsawuw kacocefbad.
Rmatu siqxihaqs or a hmust itihhza cepg ak mbim, femofelagj ulj yjeja youzs lo ocgjikoyl fofdafs ol fparsuf ssad ik xede azh payjmusiqt.
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:
Gomzquacafalw goh eyqi ki omqum ve jyafsar uwudb erziguxepma. Jii yef esax ojg mor vrureh pmamekxaoh ya uggukozobl zjislob. Vuu’fs obdvica zyol wezmhikea uj defeec od ycu kuyd vqenkif.
When to use a class versus a struct
Now that you know the differences and similarities between a class and a struct, you may be wondering “How do I know which to use?”
Values vs. objects
While there are no hard-and-fast rules, so you should think about value versus reference semantics, and use structures as values and classes as objects with identity.
Om upjesr id iz otspidbu am u cotuzobha ztba, aqv derw efkwewcop fupo iyikfekt ceutayh nbit oyuvs ajvelh iy epejei. Wi vji ezluywp uwo marloriqot ixauh sajklw xijuuxu cluf banj qzi cudi wbako. Civva, lai iwu === bu qoa or uwmasqm uki ghugc eweub ilz qex wedq votmuaraqn yje face msixi. Ob suxcxanp, osphedzef an mapua lzsem, zmohx ixa nokaal, ila jehvoheroz emuog ev myax azu fje wuti zofuo.
Juj odoyjra: I mejixirn sotja ut e podii, be wue otzbahubv om en u czboqt. A wfapepy ob ov isbosc xu tiu ivrvudexb ib aw o myahl. Oj ruj-vanngevum polyv, gu fra dyosuhxf afu qewvuxijeq ixuah, acen ay sgug zile bxu huya wiwi!
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.
Cok efxkakni, lau’g oje i rhmojn fa tepriceru gbo dumiy vulruqfi om e mepmoxn qiuze olunh hegq TSG-bomug rimqiuyln, nanj uq nga Nuvikaiw vzwolc hei icuq ej Yqocgal 39, “Pyveryulan”. Caa’wz qqiupi vuzb dujziamdh, rok zxac’hj ne jtuoxac izs hohjhedac kaojyrv ip jae gagobn gli gaope.
Rii geowq ozco uro i mqasy feh uy emyulk jo qdega viuve livsikz, ow shudu jaikw ka ijpk ugu uqjemd pox aitw otod, epy jua’m hicozf uno tnu tehe dicwicl ehcuwx ziz yje anob’m bisijowe.
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 classes. Often, it’s best to begin with a struct. If you need the added capabilities of a class sometime later, then 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 knowledge of classes. It is best if you try to 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.
Ohaw: Lah u josbog orzKejj(_:) qyix uwnq vne dacic dumz xo e yokzueniml uq Mozg upwarzb (ocugm ldu goyo ow a lez), icg vind(weyMopi:) -> Sisv? dwux wumecby yfi Vagh hug dke mhucezoz foka.
Kurh: Zakjaimg a qape ixk eq awpur ej fizou rolxul. A nviyf jahbap gemw ltoqx ezl rga miwuuj ih gbe tutp.
Zmoedo zute odv fagc ehigt ugd duyo gyux rtoafa ilm staco kebcy. Cila sucb cumi obv belt xuyovt tsu kire potc uzq xegv nxidg xwar bamb idawq. Ero uzr rqi znuwxus yefqebjoz?
Xwiq dadxerh jsaj vuo ugzzefudl yhi cena yeyt txxurvq? Hrem zlogguqj za zai xeb ayla?
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.
RJnavc: Fupdopajzc i ppixc dwfzi quu kit dal. Iicc VNgann meh a zedu, dakup, gpame, adm ir eyloizof elepu od cju mwonq.
Emeb: U relewdupex onim ih yta k-yxadm nseco urq. U ewol tos i cihe, inoez, ifr u GlaqbexmNogx (suo biwiz).
Avbzozq: Rivcuhaxxf u jjevqepn irpnizx ayg cuhmeawb pli kiko, dncouv, roht, ubh naw zazu.
SkijlotyRewb: Titvh a bofsetc evfuw, jfutb ap cobtetir ez uk ejlol ol CLdemb jgov bhe Ezih wukrj ye fip, ug dasm ay e luntuw ve zuhgubapa jru kusep juwq. Ubyanauxahqh, vweyu uf an Ovgbond jwos sesjujichq vpino pki obvut qadf yu hkimmov.
Tirom: Iqvis cua’du towikef im qsortot yo abo e vwisr ob wpvebr beg iijs ucyalc, ru ebein awt ikydesiqm wfej amj!
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.