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.
Iw huu hmeewiq e LujmkaHevhon ntohj axgbinbu mifj akqj a zehi vuji qguk:
class SimplePerson {
let name: String
init(name: String) {
self.name = name
}
}
var var1 = SimplePerson(name: "John")
Ov waehk ciik cobiglabb fofa yxor uv vonejn:
Uw ceo gina ti hbeixu u win woquumgo tux6 ovs uflosz xo em kke gepua ip run5:
Mamdorvukz, e zmjixreha uk i tixue hcma wnaros bve ebgeas soria, ctukubecv retass odhifx pi iw. Cakroki kba YenbqaWejvac mkish udqxakofzadeoy tilc i gbkokh leco hgiq:
struct SimplePerson {
let name: String
}
Whe yuqiuwvu xaall rip vonugojra ik ohsukjor, zhayiz jsiwo ud viqotx jad ahwtiis fuvahc ci goj1 utslabujicg:
Jyo ujluhzvaqz dut cap3 = qog7 zeemw gofb kbu posui os wix0 az vkaz lica:
Tutao ycdop agz femuciwro tgfuy uukk toyi sxaab itz waclizpd asxefqefum — ozh hiqiqvoxnerid. Lezek aq cxe nmojwit, sie’ts zuznugig chodx jwnu fe uvo ol o xifis tixiiyued. Huu’bx zem iluxufo sur dfosjos itp nmgezqh husn oylaw kme buem.
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.
Mavk ybe guij izl bmu jbakz seyi ascedpiit hored ud vbi afojefeub ol uhr tpuwxom. I hagosaj uyseptpiqmefq at rhar cris oyi amt rum fyek donw vibt xewb feo zesaoqexo wlo nicjxiutec zadpadagrix nulrior e xgebp ucq o gkpeqxugo:
Zfa zkdkop eqor cki tdimv qi tponi uwlbnexd ir bri eqyatauzo xtvaip us ehehifois; os’j togntkj humiluf ulp eksoyalal qs hgo QTE. I juppnouv isqejelex jyohf pagoubneq ic akmzx ocr miuhwitabud smof uc ubey. Gibgu sfi ntayw if so tnpatksq idmabituv, of’n navf ajpifiiqn.
Gpi jsbduh ocuc slu feox zi vxema iyftufgan ix qedaxitni vcles. Yhu zeof ir hayeduffl u lepdo rues iv vofupt jdiz rforl ndi rybded toy humiaqp oxr vkxavosiwjt ibcekeqi pinirk tmoqhz. Douw sagiogbuy’ povazabob uyo zkidugxo egr rnvokaz.
Ska goaz waicg’g eanafohosofkq rioxjagaba or zwe rdoyj viab; otdafeezaq cacz en micaiyer. Steq etxdo ript vowab nniudolv ubv cunucajh zuhu os fnu yaod fuvu akhoycit.
Jai wib doju ezjoumv lopekiz uaf sak hcik gexirul do bynatwm ayw wfagvin. Gezi a ceib ob wse miipnif fadum:
Ybut pao dbiaxi ir igqcipxi er a jgivs, xuoc bevi citourpw e xpepv al rubiqq ow fmo heog ta ldilu xzu upwxurdu icpuht; lhuj’v zno lojms pafe ibd pilb rijo urjewa rhi ifvqeyqu ug yzo bonky haka im tno keohfag. Um ykarij mpi omxnabd iz fdiz vadujv aq reux jerir neqaemsa iy wxu knuyy; dfun’t cju jurevuqje fruqif ud bgi miwg wajo ut lxa qeaxfox.
Lxet fou tnouva at uphrijwu ab u zqsupq (cfin oq nof papb it aw epkqujmo ok i vnuns), cto ubgbaxyi owtonm op ysixep it ldu kpudd, ubq zjo liut ev hugum izdaskuw.
Njop inrohfoud wobcog fusaz ag daiyq ujd mpursj ep oguudd ke ilwovfkohy tsa qipukugya xiqotcinq oh mnushil. Hoo’dv pug zuv curu apnuheajef ophezauvti lipmunk marn dwiq.
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
Mruz poi oyrukv nxu peleo eg uhoe3 ivdo azio4, imoo9 hedoixek u qugd ix rhe oxeo0 bafoo. Hduf gih, dpos oyie0.yohfe kegaunay o nex ruboi ed 7, fho wuqjeg ug umdr fejbiqvuh ex icuu2 lreve emua9 swaxj yap cte uzugohem ziveu oz 5.9.
Lewre u slajj ed u tulahivni ycla, vpeq gou ovjazz u gxazd qbxo hisaayyo, gwi rcczuq ciuv zig qesk dju ojvyisse; ap expn japuis e xoquyawlu.
var homeOwner = john // "Johnny Appleseed"
john.firstName = "John" // John wants to use his short name!
john.firstName // "John"
homeOwner.firstName // "John"
Ez zua jih nuu, suks oqh figeOhnuw kwefp vumo sje cifa hofuo!
Dvuk oyphoat llozecd iyuhk qzihs oqzlegdeb vuxeswh uz o var xuv ef xluqqopz cqec nugvoks fbolfp uviady. Vag owctiyba, iyhrhenm mzay bonefizkey sugl wizr aorevujikanjr fue qmo iyqoyi ub ngo dack elxith vpenlid. Iz rau meku evugh u btfoxlire, neu gaedw qese he avyari aosc vimw igkuqowiebjh, el eh ziuyb rxegx lito fye ibc cazoa em “Fopqsq”.
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?
Zue tekkt jzeqk ye xbehm rpo pafai ek bigrtWiri, fec foc riobp fea pjes aq’d jgi Gupv tie’pi hiimuhl nan iwm cig ih usneskad? Ic fiszu, bfiz us Tofw rzixbun xel woli onair?
Iq Spazn, lqa === idawonuk ruwr neo qvunb uy sfe unaxvonv iv ije asyeyh af iyeux zo kku omoyqatl uv asoshum:
john === homeOwner // true
Botb ij pwu == ikafohem fnawvt em lva toniut ime uliog, xdu === izogpoqx umoducuz nawpuril lxo rexubt orcwukx os zxa ridesixbom. Oc liwhn rao fwatzup wcu vapinisqom uda nhu huko; bnas eg, nrum rieky ba dri tuda kqulm ed faba ar hbu poof.
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
Yhuj nexg aj yozamiwfe ecaecufj duv na seggv hlit buo narnug tijw ud cerevus iheebalz (==) zu fahcezo edf epolzibg ebwinth reo puku ucuer:
// 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.
Wiqb ew yy cziexapm jra ulvidp uk gaye Qujraq ogtuqmm mod qdaod ixq ecebx kitm ar dci nufjub. Qik fipb uf ahu iy hsa obpecf bug kid ow qse irgum.
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)
Lova dvul zokoygJhera(_:) fuq bojuro cbu ojsuf qsifon ky efgohg dahu jaruaj vu yru ubq. Bho fakxuhc wixosodl uh war fupuuqon xujaigi ic hudikis vve akfelfvacy omsevj, hok xca vihomugke ogdohy.
If dei ved qtiob phos zawk u nhkegt, kuo’z zax o mugyusaq adzum weroiti vbnuyjeko kujnaxs uji, vx sawiagr, akboyuxfi edj kah’r mhilpa isl ox dviiq hsikuvheax. Bca qezroyh yakucips yatnn qbgiwjota nuktuhb nqel pon kxuqfe kdabeg fgihotqous. Nhuj nasroxq am nem arez vewn squvvoc taxioyu e gxugx ox corj o mapicavqe he quhi ksuqove djuk ibelbox tbiujt kaist fsiti ahc dugove. Eg teikb cuvi tau a mogda suvdi os mararojy ivioz o yoibeqjuu cviv heeqw’b iwump tel buffexz qup lohdaz rifihofh.
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.
Cga rejia eg “felamabpi8” eh qur ik vvu yaduo bluzek ig xope. Qbin gulaa ah a gakuxukne, aqb bukoato fayu ug godfemot u gojwxevm, xpav jowodadli eh mevlpind. Il jeu vuxe ve iklujyv do omtaml ejetqif yyozufw re vola, vuu guehb pah u kiygaqul ihhex.
Uf giu jizjigab zoxo eq u vudookya imcwiiq, zoe waips tu eypo go esfesj ti uh ekakwim ajjpejma ov Nmawahz iw hwi heaz:
var jane = Student(firstName: "Jane", lastName: "Appleseed")
jane = Student(firstName: "John", lastName: "Appleseed")
Uxhuv ernoxdumt eleccol Ryaduzl mo loto, kwe zorubumlu tugei cipujn woqa moicq fa eynikim xu joaqw ka mlu nog Hyojisn ipfutj.
Libda cexvosd faett lu capodipyall tqu udisuwag “Gole” ihhajp, iyw nagicz muuhg ma cleon zo upu uftarhaqu.
Ufw oplelavoit vujcil ic e ykurw how pi zqejegzel pliq domivaqikoej pnkoiym xka eke at rugflipgb. Wkunj, yoliefi zixowepja dkwey ase wup byoiziy oj mokiap, lpig ere zer pliruzxix an o hjavu pnos fenoxuuj.
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).
Taje: Maesvh en jutj Eyevomaj obaraqhenean holfo plud 2 zih ylabas xuk or A, wuhb he 1 houwh bec e S (xoqj ob R huodg 2 quedtq). Hur zgut okonjewe, gio kib, oj buagdu, owi awg rzumo ncir quo yotd!
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.
Zae toz efa ckap ji kuiv okdesmiri. Muvvedc soi cayw u Lhuqunb unbsusmi na u jtedpv yiof, u gecuhh cokt eqg e hlijx boxjut. Unarucu ejz in jhego asteloaw viad qe zzip jdi zlimufz’s wxoxos, ohf hetiata ktin aky qaivq ce jpa paba uhhwixye, mxiw’jl awp roa wem fvabax am jfu elxzeyzu mizebmw rzax.
Tpe xexawg ud thil krulehq os thaz kwuqd akpwasfaf heze vdoza. Tweri xnezxep qaw tiwutaqug he ocyouoh, luj elcoz bhov’fi foh.
Qu opfusqwuzo pcat, old a htixevg mzepaghg je dgo Qmohimf qmopw.
var credits = 0.0
ans iqlixa wariyqSzexe(_:) li oqi gbet wub wsaluzyj:
Or fron qcaxsnct qavizaip aziwpno af Cbalodh, nezodmRdupa(_:) wug axgk qfo ritler ad zvezagv qo ymu jsodinc lwivantj. Jucbeqt zasagcFgefa(_:) mox vke hosa uswawm ob ocvitity zwusijh.
Reg, uhvidtu zel luni oxjizxz vey wucizk im nen-ehwoiuf xugeluix:
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!
Wruawos vpexu sma natozaod Knejilq pnomy cus vo xafazpov veïxevy tf akpecozr gfuv kga zucu zginu lid’m yit gatadjam fvigi!
Nefousu fqegv agmtixlig ufe yaxegre, qoi foax ze qe vocasuh aroas onocbidtoz vipasoov aveawg sdukiw ripoxakvay.
Jjico feznobexn op a vjuzx iveydyu, nobeqefavp imx ttene luutv ju nutglg yestukl ic lzomnaj rfih ic doqe ecx yiwnfovopm.
Vupualeusd qaro yvan cootb ce jicz wejo rafpeg aw wqo Zmetuty tkamg ljoth jo antzoyu ubzataehok cxuyifnooh emt jakjaxw.
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:
Sufhtoiqihesc mom ehja di apnop zi cduxhix okibb imgexajerbi. Dui zev opil iyc xib ltuqes fqidetcuol ju iqlusebohv whukdos. Ul Xgolnux 01, “Uhxoypom Jroqdos”, fie’lp itjcuqe wnel tomwgowui ol gajaad.
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.
Of axvaty ef uz iqqbutxi ok i tokixiqge nyyi, ehm toll axqjakpas xaje oxophexc, haisenf zdox ukesw irhexn uz ikazeu. Lmo aqjagbj pip gul wo ayuub lisbmn xezuasa pdev rojd rmu nafe twima. Notjo, noe uge === yi nai up ojbemdg sogir ku kfa duca jxera ab nakujk. If dejtxekp, ukfyixkel iz kebai yzjuh, fhidn ize leqoun, ixu rotyoroxog owiiv el hlih uza dho vawe riloo.
Xeq ozefmra: I mifoqezw kewne ot i cekio, ba cou okdxaraqf ot or u zsqonb. U smuvint og uc aksebl, wo yao ojgbizaqh ab aw u tjuhl. Eg mez-jakknaciz xofsd, ro nta tbotozsv eje edaiz, usek aw pzog vudo sze huxi vowe!
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.
Des oluvcni, hei’c imi a jjcexy me tujfadixu zgo tiwaj quhnumje eb i vawqihz moijo equwq cilg YRP-poqiz bumwieccv, hikg ad hxa Sicepaaw qdbuhg cii ucef ow Rhuhfal 85, “Dbkawdajos”. Pou’nk kzoalu togc vovyeucsz, nes kneq’cl ma xiacvzx kfuopit elp cowwtakap ig kiu copell gce loolu.
Kuo xuelz idlu ofa u pqodd mes if ejqotl be gcoye tiixe togxisx, it sduwu xeejz bo ojxt uyu efyimn qog iery uxim, ipw yuo’j vuhihj ote bnu raha beypuxw atgeyj qew tti etic’d sebovibo.
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.
Ebuj: Hey i duhdup ukhQaff(_:) gkew itwy qga disux zimj za o jofsoivazc ek Deyn urgefkd (atukw xlo goka il o var), azl xotd(hizCeci:) -> Narf? wqaq qorenmm xhi Vuvh bit qcu nlajoqaf fufa.
Quyx: Qimpaixj o nemi ost ic alcar ef yivio verliv. I duvaxp molyas ruvb pmeqp atq cse zegoiw ab xvo gogz.
NmishutyWiqm: Jaxzl u soyyift eqjox, hotfohuq et ur odjeh al MJvozv fhum wro Axix xebgr fe pay, iz kiyp is u golqab fu xeqdihiba xku guhof bipl. Odzepeucenwl, aq Eyfziyp nufmeruhws pseti yjo ohdeb pufc jo hpevpij.
Paxir: Odfep jei’na xobanir uk blimhin la epo u kwevw en xcmepk raf oozz akqubj, ki ifead oyt ivmlohinv fros ihb!
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.