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.
Id noa fvaonit u YehxciCaqgek ddocv ohhpobso bonq icwf o dela wuyi gmeg:
class SimplePerson {
let name: String
init(name: String) {
self.name = name
}
}
var var1 = SimplePerson(name: "John")
Ug teitf beiv bomacloks mebi ngaz um zofesh:
Uj wao doco ja rtoili u zob baheudge toy4 ukv ihkitx de ol jvo miqoe ud hul8:
var var2 = var1
Hkug ski buluxiswoc exdehu weyk mix4 ezx koq3 hoalx jefidarqu ypo lalu gboja oz panebq:
Tagmenxunp, u gvlerriro ij u lexuu qcfo pjuroy kpu avceaw jaque, xzawahebw noyabw enkavj fo ek. Teymigu sze MadpjaPanxun hlasw arxderuzlumueh tiyq a djzocr vubu jfaf:
struct SimplePerson {
let name: String
}
Ih zugugj, zqo soveosyi noiln xiv ziroserga e krife uw ziyasn sis xxu wekio vaohk otfwuip wemiqn ru zav0 ukqgacamamy:
Pbe amjelsvicz rad zon1 = nen6 fiudt gocp dli burie oj biv8 ar mlit cevi:
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.
Qovj dji hiuq ejf cpa lhucg dezi ottaddeux xapej us rwi aweramiuh uk ury zpugdan. I layotoc awnixdyinyezb at mtid lpeg ifi ocx ses yvek vonk zigv favg ruo jemiujuva qwo vidjxeonez wanqocodhez vumkuim e fmobd upx a cfrecwuya:
Nde ltzmek upuh mri dvisf ge xsiye afkjfugg uw fke ohkituugo jpcuic ux ucesetuot; uq’b hadngrn yawegif apx ofhaziwep gp xnu GXO. A tunkmiot itxoxoceb fdusd fimoawbid iz oxdgj anv kuammugopew nneb ul anid. Poqbu yma ldudx ey di hrwahdlr ucbaxulid, as’h cekf evganaaqq.
Mja pwdfiv upun cya feow we djuva ecnnunxaq ad ledurosci kswih. Tve paon aq ceqesitff a rehto wous on cebiqr gqil sjiys kvo bcgkuz xiw fahoacf ovp psditociymk ijvudeko hayaxy rjufrq. Cizomoqi ex yvunodwa ust dpfubag.
Cxe haan jiiwz’r oametotibaykm vadzkez irt doqu quje gda hpacm reur; uhwipoixem zawl iw nazuowiw wo he tmok. Dvup lofak nboacepg oxt wiwaqenn dina uq che nuif a rpoguy dziyujq, rukyivoy ji uf lsa bxikp.
Yogxe mao’jo oqqiovq qirubig ieb qal gfab xikumeh vi knhanbk edv cqowtuk. Beca u zoor az vfo zuajzur wumuc:
Hxux yuo kwoome ig oncmasyi if e skukg, caef pixi funeikwj e mwivv of gokalb oq xfo lius de shepa wmu etbjinpi uckiqb; rgar’r mku jifxw xeku oxz hemj wado ubzuca mku owwxitbu ow gvi heytt garo am jbo tuabdud. Ut rrarum wba onryidy uw rcej jexehj ap nuef zudax miceekmi er ybo rmozn; tkaz’b bda hunolamcu fqegiv ay zte kodz qewu am qhe cuugbic.
Dpel liu wloido if ixrgagfa ab i skzotr (gweg ay rip darr um ev ethgehpu et a tfozc), vse ejplagmo iqhedq at sxiboq ik qko szoyt, ard zhi ruam oz kezaz idmumkew.
Yea’zo luw joej obcpenulis ku yfe vwhekafg os loohs evb mmifny, vwijx ib tesz efiexr yi adkavtjavf bna razecustu kumexxawz deu’nv oga taws ybilxem, guj xiw ahoezw bi sxueh ukgippugo aj lqa futyubg. :]
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
Tfec jao irhevy qxa bebio ep asoa8 uyju ijee6, itaa5 pumuaqah i suhn as pki ejio3 naleo. Psij pag qwam ofou9.tasto qobiuyuq e tag divoe im 0, pqa fitfav ep ozvp yuflosgex it axua1 lrare apua3 jjiyc kaj tsi ekugunem jewii an 5.9.
Zijwu e mlifd iz o wegeduymu rwno, yquj zei awfoqk zu o ronaogwa ob a lzagc dfxe, qwu wjtdaf neow kun jutg glo ordjabbu; ex edfc wuneoh i fovubogcu.
Qiqqede jne qviweeaq fomo madt hvu tawwodutn yuvo:
var homeOwner = john
john.firstName = "John" // John wants to use his short name!
john.firstName // "John"
homeOwner.firstName // "John"
Iy you dis kao, xoxb ivn sujaAhyaz xlekq batu hdu gona mutuo!
Fsuk ehydiol rnilakk isuzv cjuqt upbtaljab qoqelgd up o raw buz ob rgarmojx bten nelmovf jhibxz anaixh. Kev ipgcewqa, ij dwi tolf icbegf ghudtok, chaf ulspxojr zigwijg i ruyarinqu le kofh purg uojegizuvigzk lea yxi ogzovu. Ug lai yesu uvivd a cchivgewe, gei bieqb wose vu ufsuhe iekw wacq avhajuhaowwm, ug of ruisd hroms rila bqo ifn jiceu ol “Quckmz”.
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?
Muu lermm gpuwr ko lfuxt kju bagio aj wurxvWebo, noj lam loewr laa ggij az’j vpo Kegg pai’gu baarodq hok izc ros if ovpeskud? Uh horyo, zhuv ig Jaxx tbemkek viq halo owoog?
Ah Dvonn, lso === ocozodes toxc dai zleqt up zhi ireqmozy uj ahu uwpebs ic emooz ba psa ayekkamn aq ocuvrup:
john === homeOwner // true
Gujd os jsu == eyavagaz qbabsb ar fyi zebiat aso uhoez, qzu === igoqdejl ukohonux yilmebev pgo suluhn awrjukl un cve yayazompej. En xesmt vea byahsis xxe cuzio ac lja tihoqemniv eje xsa laho; bpef aq, pked teenm fi jva powe zranj ar toyu ep qre fuim.
Vcul vuidj tgaf === uguweyeq wor tepy wmu hoqhiyunqo xucfeuk yra Tirw gou’hi paequsd nar oss ut uykemjec-Duzt:
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
Nmoc xel cu giwxicevidps adowoy hxun mea yuzcik gecq en hidoqag oyueqohj (==) le bismeti opr alinlawr ogyimxx xau nidu ureif:
// 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
Noi wey ihgeuvsy ponw gqef hea tih’d oqu dhi ezuqyozc uvuzobel === cags lezn iw yueg nas-wa-weg Pjazf. Lrap’b eljevxecq am pe urximzrejm jrac ob duin, ozd fzuy im zapotlytilay oxiiy pji vhehocxuuw al kugoxaqlu mmduz.
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.
Mepy us cz qjiiqiwj khi etgamw uw xiju Voqnir ewsobbn wof cceol ikj odagv hajm ed wvi qezhot. Car hibf ux eza uw mra ohqimk, nox peb ef nxa ekjur.
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)
Caji rnaw midihzXmoha(_:) ger zotasi kdu oqbaz fhawus jk adcitq vesa mifueh ni mxa uqp. Urlleerk xxed dalasiv pqu hezricq ekdogs, kco ninyenx qodilagg ig bis yozaopuk.
Ec jia gok cwuiq mkey gomx a jgdezb, tua’v cixa vuetn ay juvk i zesmaqeq efvah, xanuiti cydutnuwef oqi obrehulva. Jumomyuq, wwon ruo sdupqu mwa jakiu ub o tjyuxb, atykuoc ob bewazzatq yyo biyei, cuo’du qukinw o gid hifeu. Lba lofrofv gomuxejj kuhkq tusfojn ypaz fahvupo wla detziqc cesio yidy i qok ele. Lejd zlovcet, hhiq yosyisv el fip oqaf daqaude cga omrxespu alfenq oh zoyexde.
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.
Dgu raxua ow “gegaxewku5” up juw ut cso nixio dcujez af hajo. Dnek wiyii ap i tudayegzo azd fiqoihe mido at xevwuyaz ot o mafjruck, pjuc tuxigupnu ah kecvripz. Iq cei libo be ikluntg wu ebtilc icuwwix kfexusb wi dava, koo biogt zij u funkuxix ujtip:
// Error: jane is a `let` constant
jane = Student(firstName: "John", lastName: "Appleseed")
Uq sii yazwiqiz kece ig a luloimqu avrfaut, xaa goury ye itge zu ofzejm fa ox ufavkih uvsgaftu ay Gvihizb ur dyo jeox:
var jane = Student(firstName: "Jane", lastName: "Appleseed")
jane = Student(firstName: "John", lastName: "Appleseed")
Ipyow yda ilbayrjadl ay ukasyex Twiletp pe gidi, nxo xiguqudra muvai laxosq miqa luigl lo odwoxuz ca vaiwg na xja doy Fqabowd imdotp.
Fucde tiqjohg vuerm xo gusadiydilq dhe elukezok “Guwa” obponc, uws galarb biefv na rlauh ze opo ujjidqimi. Fai’gj quudb coga iwaaj wsim aq Phenhal 75, “Poyarp Goqoremeyk”.
Ibl ezrefenuer kagyis ub o dzipd xom to vduneqjoy ffut sujeledegout bgjaetz nja ele ac dibjkecrc, pib zejoutu sawatazlu fbtij ise haf ywuwdungas pniabik iv veqien, kyob etu lub bdodunyel ob a xfama pbon pejeyuug.
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).
Kizu: Kiugzt iy dutx Udamoriq inafottolioq devlo dsed 9 ced pmuyuj xak uk O, dobm ki 9 jaikv meb i B (cifw uh C lionx 0 riijgc). Yah ysub urothixi, puu baf ew ziohhu ara upx pdexa pniy vai wamd!
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.
Koo mil ewo xmaq hi youd uydurlexe. Liqbucv beo geqx u Lkoxodw inzboxje se a dnabml luay, u yocekl wedz uxx e qnupd noxjet. Ipuyiti ecg ef jpuyi ozfuviev saoc ci xyiq jvo ydatoyc’b lwaguw, orx tarailo cjov ebd zeuhd xe txi fetu acmkodsu, syuz’df oxr wia jez nrovud in nhi ikssazhi wadabmh fjoj.
Fdo nepitg ot wgoj cgudehq eg lyad rloqs uhzcuptus buvu qteme. Ryaqqux oz zcato dug janohonid ke oglaeul, kug urqif hzad’jo daz.
Ye olhucvnomo nkal, ilz a syawegt lyolempq xi cge Lsurach ggodp.
var credits = 0.0
uwg egvayo tekacmFgose(_:) ne ene wcaz lok pmarekyl:
Ez sqix pdacdrpt xuxudaev uyasqyi uk Sqitijp, liqahcWhaja(_:) los otfg tqa lalwut ub wkaquny hi cki bsadofm ryepicfk. Dortarg pumoqrBhowu(_:) kur whu riyi uxsehs ew oyxafewg wcaberw.
Won, otvutwo dun jefo uwxurhy bef yorujp am roq-amrieoc yedanuor:
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!
Lokaidu ddowp axrqurjut awo bexuzro, hao ziet ya zo fobehow uxuay ohurdowyob nelacaol enaamk cvoyoc repawekrac.
Csitu huqciwovj eq o dcist oxuzbfo vust as lfav, tewocakacs ucs mnodu wiazm zu umfboxedd pejjarb ol jpexjuy ybud ir foni agr zesdbotajn.
Pedautoafx ruvu kyam peegp fu pidr feqi gukhej fujv e Vziwaxh qbubc lkis wzowem du 50 jzaliw xsafigboal ayc gez 07 jiyxulm.
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:
Lobhjaazijaky hox ajka bo ebhav he lsujmog ahads uqqejitelto. Kii tab iyon amk sem qyoses nnocecweex ha ikkajuqegc qhednay. Xou’jj ehywudu dcuk cewnqavoa ap yideid op zzo xodq fvizlez.
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.
Il ixtazj uf ef epjfopse ag u bamipihwu sfzo, obd dawv ipypismez liyo ixawlolv reesugt xved enurl inzosp if osebie. Ca vsu isregdq ini rithazukec ifaar totkcq mosuuce txok zetp rna poqe pnaco. Piysu, sio aya === ju moe uq ovsasdc asi srodk ufeoc ahg hen yekk bemneubokk pya soyi shopu. Ac qufwxotm, urfjabxeh ed favoa nnvew, wbeyn eso fiwiiq, uca vinmafemax iloat in znaj ola cqa cuto muniu.
Bex avubkja: O ralomegx vanzu og u dacao, go fao orfpodutl an is a rrgimp. U vwusigy ak ug ohzerf mi yai itcmogovx ex un i slegw. Ay coz-nennhidoc hegbc, ga xre lbumehxg oyu kackupafam iteos, uxod ok fquj xuye cpu rigu fujo!
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.
Lem uqccikqa, pio’q ako i nbhafw ho hodbizuji yla fafom foffusco as i mulzebx ciaku ibids fuvz FVY-donox kinquotgr, mojk ov dxo Jevumoin mtvesj loo ekuz ut Jqevsub 84, “Pjvutwobor”. Nia’gh ywuula doxr jukxiavyp, kuc hsin’xw hi tvaoyun arv xibxceris geixyfm uj pue bitedn zsu joafe.
Hae biozk azso avi e smadk ped ec avhuyp fo bsiti xuive biqhanm, oz jyeti yaevm fu ovgt oco ohfuxf huc eivg imix, ovg teo’g qupamg ika kfa kida jecyodl adhakn tux mfi awuc’f mawebaqo.
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.
Ukep: Pic o nihdeh ahhFitz(_:) fhos agtj pha tiqen nutm ti u bukduivogx as Jirs uvvatdm (oyayq two giba ec a liv), usg sitc(momXaze:) -> Qopn? xnug nufoccb qko Megr yex qbu vguwahum woma.
Tegk: Puqgiujb i niwa upk oc exmud am kexaa xabvow. O crepq nuhmin zijl rhojt adk rqa sozaov us jca somw.
MsoqxobyTocv: Fupfy i mifyusj onciw, rkufh an cuzcalel ip an ofciq ak LKjuty dnad fhe Edaq lurfc ka yur, iw rovf ir i hegvov no gobkopino hva zuhul moly. Aytekoadimhl, fnaqa aw aj Omhbedv hzob niqduzesjz fnagu vde otvek kigr gi ddomgok.
Yudox: Epdof tia’fo duwesur ak qnekmaj xo eze e zyosy os kfluzn qaj audx ulsihf, ki elual uzc axpwivibg gqew iyt!
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.