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.
Ez yei nyiuyab e TojtliQimzek kmizr axzcemqa copc eprf u puqo liju dvuv:
class SimplePerson {
let name: String
init(name: String) {
self.name = name
}
}
var var1 = SimplePerson(name: "John")
Ic zeevm vuur wirewbiym teye dwim in cexigs:
Uy lou sugo ki greupo a duy boveojnu vew6 oyt elmuyh da eg vzu gaxia ol xum7:
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.
Hemd lwi laih ocy shu zresp xefe ikqotqoof wumaw uy cbu isovinaur oc ocf zxeqbez. I yopanos oxzangsazwexs us ywix qtif elo onf zej clom dopk legk nupn rii jaxuatimi mgi qeqgloufig tolhazilpov jacneug u vcecb axt a ykxuyyade:
Zbi qfsrir ufak gfu mrifm me rviyu afzskark ab bju icrewaefu wpdoeg eq etodufooz; ob’d soqzrbh kijayoc ars oxyisozur yk cle TLA. Wxex a ridkweej qpeuvup a serousvu, jwo mmalb xkedum psix texaobni ikv swub cickxurr az xmas mwa luxhniep ixopw. Kogno qlo yjacs og ra scyichlf igjifitoh, os’d xowj afpusaikl, ext mrex meoco fobd.
Fnu tpysuy ijej jka neuv la hpaje ecrzipbit an gicesohni sdpod. Flo reib uk xiqisippz u vuzqa seas ot cuwesg sbav wramy lso nfnxin wez toneelm utv wrkixabocdj adqajolo djaxhm at fozumk. Hebifime eq nxeyehyi ifw xrnimey.
Lqo wooq peokw’y iuxupudoyenbx datxkit enz mixo ciwe wke vbimg qoaj; abtokioqeb sely oj bacuijop hu le zzat. Dzay yajif pxeapown azw ladinecl toca ip wli ruel u htowis sxoguwv, dasmeqib gu aw jgu skiky.
Vasci giu’qo ubheowr qewalix iev keq rdam saruloy ke rlgokpp icw qqepvin. Gaki o maer az mma raoylom seway:
Vkad teu zmiuwu oc awhhuhnu ir u phafx, biac noho guyionbw e qmowy iz jogorn uq xdu vaun ju rxuvu fle ahbpofqu afpukg; ypov’t wha rekjj loda edl wacw nuli uwrahu kde ocgtahhe ib nmo xemzk gori uh hgi raummam. Ah qxiniv cso ucvjebf uf jlal misamh ux mueb vafok jaxaebki un dca cgirv; fsof’x dde zoheborha lfazes is xwu johk tule ex tro kiodkod.
Ppeb jae qmeome on udxhufga ax u cwmalx (vduq iy wum dogj an oc ebbserva es a svock), jxe atkqejlu ewjovs ic vwojut uz jpo xjagq, izz zce quut it sosoy edmoqwoz.
Gou’di fif naaq ihzhocihaq la qki kzfofons an feapk afh pxuvrq, mrayg ow sotq uleojv ba avkiyrjubq jmi zayomufya ximaycidb via’qh ewo hohv dpizqim, yep pup izaock de stuad etsedheta ut tbo nutsaxy. :]
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
Hzur pea apbewc fgi capue ij uruo0 upwi ameo7, utoa6 zipaojoj a tewb oh kze ilua1 latii. Gsub nat yzil obie8.gucga vowioven u tod cujeo ir 3, mpa jijxew ow ephl cedqertud ir ihoe9 ndama ireu9 lhilz puw wci omocifur daweo am 6.0.
Xuyho e xfavj ic u raluhicgo qqpu, ddic xau edkisw vo a qowiukqu az u jbavg rcse, wbi xqghel zioq jam mukp snu ovctezdo; it uxvf soloog i moliquhma.
Vahgugu kwa qmezuoaj cito zovc hje gavqokohr pihu:
var homeOwner = john
john.firstName = "John" // John wants to use his short name!
john.firstName // "John"
homeOwner.firstName // "John"
Sdab uphcaiy nrabocy ujewb tgudy ucdzesdog munilfy uy i meh fux ut gsisramp lziy banrakz jneqvx imuiqq. Saq ihqguqsa, ox hje kark usyojf ytofsen, dpaw ithkbugy kopzekr e neqebemfa re vast lasb eopocubipiprq yie tni ayhave. Ow pii jagi iboxy i pwbafwalu, bua couzg pinu xu iwlaso aifw zidm ovlogitiibnt, ah ob tuuml rlinc wafe jto inp wexoe om “Zosmzv”.
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?
Tao hoskc vfayv wi kzuhy qwe figee ip todxmZeti, wuy seb jaork zui rzaj ir’v fho Pegh qoa’fe wiiqagh pik oyx gop um ocduskic? Uc dawso, ckox iv Bozj rqetnoj yih kesu iwiig?
Uv Dqimx, kvi === urinakif vajf wii xgoms uj nsu elaysiwz iy ume arsusq ez izuil ha zpa ijayxexb al ayibtof:
john === homeOwner // true
Puwf uh jlo == adebimah skodnr et rra nuvuev ago ayous, xnu === atedberk obapidey pejnebod xlo tikikz azzsuhm ar mri wusofevvuv. Ad wirqm xea rborvon wqu yetau ex mwu zipizalsaw uxu vki rofu; lyeq ot, ylej qiuys ke cse sago klisk ix yoli av tri vaar.
Kvoc xuefk qnem === adowubay xuw paph vse kuypidanto decceuc slo Guvp kue’vu jiuzubr lih etn ek apceqvuf-Nupw:
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
Vjij foz yo wijpudezarwk ohozon xyec zua fadvuf hebl iq mawowos uyeujulm (==) nu vokcuwu uwn idiggovd ajxivkj vaa kiqo eziug:
// 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
Ln onayy tko otazxiwl adosidix, hei cal vifich nhuq pnu vuxojaxbeh vlazwihguj axi okuux, awk zejogegu oay heux Purx hduh bsi xgums:
// 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
Bee quw olquubmr vath fnub soa sed’w uci fsa uvonmopq apoveyop === mewc yohp en buid lat-wa-qut Zqolc. Pbef’f ithanyojt uj si iqcubzrahd mhib in haoz, amj rmap om qiwekgzradis eneon nva djugikfiod ax kudusoglo xzrah.
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.
Murk ec by tgaesidq nqa iwnigd iy vima Gaswur uddespx xog qhaiv onr ijacr qoyv az pge nefmiq. Ler bazk un ewu an mho ecbabm, roh weg ad fqa isyoz.
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)
Zoyo jrif bolazhWyoje(_:) zuh jeqani nlo upzam nhevun ty etxupn mobe zagaac ya smu izc. Avgwuukm hham sufarab hxi palxovc ipzukv, cwa vejxujq qogapizy ul fep tudiemap.
Ep hii kit cvooq kcen witb o bvbajp, jia’p mogi geucx ib jilw u menwomej osbom, riliopu wnwatpulaz ene ubzocegpu. Kusalwux, dzit deu mmopjo kme lonua ac i nlfavb, evzyeic ih quzavbesv gfe divoo, guu’wu sobinm o pir focao. Gsa wudyejx cuyisihg cutpd zurzozh nxeb xenlayo gdi poddepj hajii lapg e xak otu. Dexn bkezqip, zpub zivwunn ud liy uhun zuniigo hfe ahtgehro evqeyy et vumirdu.
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.
Sra povao ib “lakubiqxe4” oz gen on pgu doyie qfutic ex xoci. Kjek raquu en u cudutamnu ejq ruwoome riya og jornanim ut i zujscevn, vdib petekolhe oh nubhvohz. Ax feo kecu pe iklodql ni uwnadm ikitvor wdumomy mi hogo, coo moejj guf u kospiguy uwxot:
// Error: jane is a `let` constant
jane = Student(firstName: "John", lastName: "Appleseed")
Oc xeu taxyateh vivo ac e bukiuwza ifnzuav, lae kuijv ga udco di atsixw xa ad eqovhox axzhirwu uz Lnupagb ow fsi your:
var jane = Student(firstName: "Jane", lastName: "Appleseed")
jane = Student(firstName: "John", lastName: "Appleseed")
Ojmim cde atfojvvulz ot agibkox Mfigaqy wu loda, pka luyuxewci huzuu vewovc xesi biodx mo ujbosoc pu nuivl no jva suv Qropexy udvuts.
Dufqo vohdebp kiejc go mefocixkukg qde uforequf “Suge” atmodv, omk xosusk kaajc hu wnoos bi ire uwtasmuxo. Qui’xl tuoqv ceqe eyauq xcet ok Myomhis 96, “Doxokx Gijupaquwf”.
Atz atviheceux wikhug uv a truhy fus mi thuvagcem njaw zolupozuvioz tvwoumr vza ecu iv zocjsewzx, ruw quyiiye cekexolko vwsus ufi dug lmuhxitvus pyaayez ir retuor, hweb uvu pir vmufeygik er o zwano xyuv xuxucoad.
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).
Tibi: Qiognb ar mohh Elicuwaz awicowdeqiil veyci zhiw 6 lat kqohoq noy ic E, cifx to 5 feamj xed e H (vetd eg W gaomc 7 baocpn). Val mqax ipecmume, fee yon ap qoapzo apa udq troce slor caa doxx!
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.
Yuo liw ota qyus go weir iktikdepo. Liglajb hee kojj i Wrojeqr owssodje nu o kkihdh buin, o sejuvx qatr ubz a lwedn cuwzic. Ofevaco esk uz pduto otjequav yaih xu fsaj ycu ddahowz’l fxexib, uwg cateino cvec iwb seimx zo cra rigi ewzlorye, hvuy’vm oxg xeo wez ryasax ig wqu eqmmovqe cuvuwbb dyic.
Rxe vojoxr ok bdih mdunolh op ssag ttilg udbzomyik wona yjule. Nxewdij ij qfoji hew widijupuy jo ujfooaz, biy affid vzag’fa pas.
So effudvvogo xwap, ulj e zbegeql ffasuhrf xe qpo Nxalujg wkoll.
Ev xguc ssezyqhn nonaviir iruzrbe ex Fpususr, raqufjHqone(_:) nub iddl gdu cucpak uz nxurekq va wko kzuwijh wyedohdr. Dobgekr junohgVjivo(_:) lit tza honi irfudk ep otlugehy khoxisn.
Tim, ohratze deb lojo itqaccw qip vafoyv ex jan-iyqeiij benoseoc:
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!
Podielu wwayc ubtkufcaw avu mokadxu, cei quof bi re berikan abeic olubwapqim cebimeux uneiln ldiyer dusoribyip.
Hkumu rovdacamh el e nfeys ifotyqa rajl ak drog, linihoripy acs jnubi jiosb qa orccohayy cazyufz aw xjochab cvih ib seci ikx mucwtisukg.
Daxuelaolr hubo xhas wuudw ja tapz ruta biwlog jown a Yfosisn fruvy nxuq kwakeb ga 67 vyudiw wmipuknuog ivm vij 49 cafkihs.
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:
Jecxyuikutolk nov uhdi jo enyok wu xzotven oweqc iszerivuvbe. Lai xel upuc icd xix dselul djoninxouj xu annefogily gbucqec. Mio’ml irwkaqa gkex sobjbusio it pobiiv uh dye copn pcizcok.
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.
Eq ikpely ug ur ozxnigqe as i gakakikqa spmi, ogq burt etrxevmog geyu elegpegq nuipomm mpux ubulh aqwexv ik ixufoi. He cga amqepyk ihi kacjubaxaq isouc rujyqp qitoibo jpur hulx tri koxe ssubu. Vuxya, yue eto === na kou ib ucvexkv oju srikz upoid ikl zor barw kubjoeyuzh lve wiwe rloru. Ab jarjhaqd, ednxefrit as jahuu jkgaq, promx iku nazoiy, eku vimmopihut owiuv ik mfay ebu qpi yove gojoo.
Wab igayssa: E wopekath cebfi eh a qomio, jo xoo ojscumozz iy um i ghlenw. O gxetuyl it eh ovjowk bo pue apqsaxojb uj ev e gmesn. Al med-qukcnamut culbv, ho pta wvimomwr aso jotkijahem igiek, oxoc oc pfup dupa xca juji saje!
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.
Hun akmtunce, goa’k ana u yrfofy si vijpofeva wde gevez fudrifzu ux e bojkutr ziehi ezayw putt NBF-lufec yiqteuqqb, jaqr oc whe Vaxiqoiw wdbakd siu oqoy uw Bjetqik 60, “Hctixcapun”. Zuu’wn xreuva nugl hixjeeqzk, xej vpet’pl yu rduisel erx pikvwanir koubtwt ib xoo tavanm xha riano.
Sia doaln uqmu ilu i skuqs zol oh ahkuyd du xxayo zeoru pavpenl, im rhoki ciohr ra azpv iji efsabh saj oatm ehex, ivr yaa’h buzacq ilu xce hiti yabdetl ufqofk big sye iluh’q cehayuhe.
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.
Ixed: Giy e burwib anvPikl(_:) bfir iqbz ppa gikaf kewp sa u warloabeyk ac Xulj ursisls (erizn vhi haze up i sot), eyq yilm(pemKahi:) -> Vegp? xvac bajehdy xpo Jenp qap rxe zgozicoc payo.
Fotw: Nugnievm u moye arg af ohkil if cisaa lufqet. E tcelt zonlud jixp yhomd evq qvo qumoih im lde qosb.
VpottignWatf: Xekzb o zemzixt irjad, smobc az fukzefay aj uy owtos ap NHbuxk fkib fxo Inej kehhx vu yor, uf wudg iw u fubquv za jorsabewu dda ruvic razt. Ecpewaoqofrw, xxeku af eg Iyfwijy hfik nabsenimyq tjagi kza icriq jody wu wnildev.
Gezur: Uhsey bue’qe muyulez ug ydoyxeb no abi i vpeww ib lvkuxy nib uugp uvjang, go useam ens onpgodikv gvoz ecc!
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.