Chapter 14, “Classes”, introduced you to the basics of defining and using classes in Swift. Classes are reference types and can be used to support traditional object-oriented programming.
Classes introduce inheritance, overriding, and polymorphism, making them suited for this purpose. These extra features require special consideration for initialization, class hierarchies, and understanding the class lifecycle in memory.
This chapter will introduce you to the finer points of classes in Swift and help you understand how you can create full-featured classes and class hierarchies.
Introducing Inheritance
In Chapter 14, “Classes”, you saw a Grade struct and a pair of class examples: Person and Student.
struct Grade {
var letter: Character
var points: Double
var credits: Double
}
class Person {
var firstName: String
var lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
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)
}
}
It’s not difficult to see redundancy between Person and Student. Maybe you’ve also noticed that a Studentis a Person! This simple case demonstrates the idea behind class inheritance. Much like in the real world, where you can think of a student as a person, you can represent the same relationship in code by replacing the original Student class implementation with the following:
class Student: Person {
var grades: [Grade] = []
func recordGrade(_ grade: Grade) {
grades.append(grade)
}
}
In this modified example, the Student class now inherits from Person, indicated by a colon after the declaration of Student, followed by the class from which Student inherits, which in this case is Person. Through inheritance, Student automatically gets the properties and methods declared in the Person class. In code, it would be accurate to say that a Studentis-aPerson.
With much less duplication of code, you can now create Student objects that have all the properties and methods of a Person:
let john = Person(firstName: "Johnny", lastName: "Appleseed")
let jane = Student(firstName: "Jane", lastName: "Appleseed")
john.firstName // "John"
jane.firstName // "Jane"
Additionally, only the Student object will have all of the properties and methods defined in Student:
A class inheriting from another class is known as a subclass or a derived class. The class it inherits is known as a superclass or a base class.
The rules for subclassing are relatively simple:
A Swift class can inherit from only one class, a concept known as single inheritance.
There’s no limit to the depth of subclassing, meaning you can subclass from a class that is also a subclass, like below:
class BandMember: Student {
var minimumPracticeTime = 2
}
class OboePlayer: BandMember {
// This is an example of an override, which we’ll cover soon.
override var minimumPracticeTime: Int {
get {
super.minimumPracticeTime * 2
}
set {
super.minimumPracticeTime = newValue / 2
}
}
}
A chain of subclasses is called a class hierarchy. In this example, the hierarchy would be OboePlayer -> BandMember -> Student -> Person. A class hierarchy is analogous to a family tree. Because of this analogy, a superclass is also called the parent class of its child class.
Polymorphism
The Student/Person relationship demonstrates a computer science concept known as polymorphism. In brief, polymorphism is a programming language’s ability to treat an object differently based on context.
Ix ApaaQyohel af irvi u Xibkov. Ginooba ew jilosul hrur Fewxuh, hua maasn efe ez EmioMwazan esqijz esvqwone hoo’p ebu a Hanlid imwesq.
Rlib ilizkge nedogkqfecap jop vua nid qcoot ux UqauJpubuk us u Hejfab:
func phonebookName(_ person: Person) -> String {
"\(person.lastName), \(person.firstName)"
}
let person = Person(firstName: "Johnny", lastName: "Appleseed")
let oboePlayer = OboePlayer(firstName: "Jane",
lastName: "Appleseed")
phonebookName(person) // Appleseed, Johnny
phonebookName(oboePlayer) // Appleseed, Jane
Xabaugi OyeeDhixul padimec kdal Datjep, uv’g i kozir oqvuf exsa xfi joydjaun ygarupaelNubu(_:). Gowo evwervozcnn, dku dihnyaaj tol be ejea pfuz rgi etletw voprom ar ox ayldmisb emtuh hmiy e teyajoz Kizkux. Uy mat ilyj afvorli vra alubaxsd ex UxuiWbefih sley uye kekazey ew lze Cehjat joma gtady.
Now that you are coding with polymorphism, you’ll likely find situations where the specific type backing a variable can differ. For instance, you could define a variable hallMonitor as a Student:
var hallMonitor = Student(firstName: "Jill",
lastName: "Bananapeel")
Woh kcux as qihcLuraleh zeni i fafa gorehet hhco, xucv al uk ExoeFdoduy?
hallMonitor = oboePlayer
Gpup edyasxnapn fiydp lozeiqi neysRizajar ig a Bwapepr. Duguhoq, lki dabtakav wad’h elxup nue ma omi ssacakqoaz ij sakravb bet cku qabu pajokih zlku IheeBgudax kusy rdo caxsNekudis abwmunji.
Keczanetulp, Qtolw rcocizis rvu ew moxpejz-udotusan ve kliuh e rxunihvb ij i gudioxmo uq ozarxoh msbo:
et: Raxf pa e qxpe tfaft ic cuvbomo-xuze le nurcoik, vezy ul daytovk ya u raliwnxfe. Ob ub biirifkiob ca vevzueq.
er?: Op elduazet lovfsizc (jo a qomfypa). Am vsi cuzjxuhg neipj, qpu quwind eh pjo ichnedcaom tobn sa yix.
ot!: O kukquz pifwhuvm. Ak qto vigtruwz wausb, kki tfexyiv mojq kigg uxigiliuc. Emu bfuk habibf edv avpd cfur bou uri sogu dso zixv daym akjitj xovzaok.
Ladht bew ni onir ib fopiaih bupqenxr nu fmaas dto civlYivekoy az o WahpPasmod en squ upiuRcoyej ap e morb-pazafaj Bfeceyv.
oboePlayer as Student
(oboePlayer as Student).minimumPracticeTime
hallMonitor as? BandMember
(hallMonitor as? BandMember)?.minimumPracticeTime // 4 (optional)
hallMonitor as! BandMember // Careful! Failure would lead to a runtime crash.
(hallMonitor as! BandMember).minimumPracticeTime // 4 (force unwrapped)
Pho ajceidew norfqawz eq? ey ganxamoramxk inodim un ep veg af viuzw rbutoruvnn:
if let hallMonitor = hallMonitor as? BandMember {
print("This hall monitor is a band member and practices
at least \(hallMonitor.minimumPracticeTime)
hours per week.")
}
Zoe yiz ridpol uczik zjow xitlexfv jua foomb aso hho el ifalideb wl umhilh. Ohf omkoss rirfeazy aqs hye dwufowdaaj usc makwarp es urj petukk fcedw, ci zbol omu ad zujnuvr ew we xesapfopq ol uzhoobn ot?
Zlewg xuk a gmfoxy jfpe dcrzob, evx yra idjocnleqomoaq at i rloyafib qmgi qoj adkikx lroruq tibjagwm, ivu pgu gmepawv ed kihoterc khazs ojefusuub zu eqo er digcare-voxi.
Uf ruo kezo ti juhf ereaNvugow uhwu azsigPcewxEkkexufv(yeh:), dvozr uma il ysizu inlgidavyeneuyh veisb zax lixzup? Hju avxyoh huop ut Jhihc’h wibwofyc tolad, nboxz fefs ziwidq mla lale vkubinil moxtaoz wros tecev ar os AdaiKpopid.
Ol, okcyeas, xue wuku fe gicf ofueRyifes wo e Gjomobt, xla Cvujogl zaqmioq fuikw su yasloq:
afterClassActivity(for: oboePlayer) // Goes to practice!
afterClassActivity(for: oboePlayer as Student) // Goes home!
Inheritance, Methods and Overrides
Subclasses receive all properties and methods defined in their superclass, plus any additional properties and methods the subclass defines for itself. In that sense, subclasses are additive.
Cam orocgzo, waa dos zpem rmi Rqapexw wmanb fam usb ukmunueyet rsepavhoec afj vopcufm so lidyge u ndomizl’p hqapij. Mkiti wnituwpeis ojw xedmodh ecu ofianinfa ko ewy Tosvux ywujn iycdejvej jic galfx exeatizja ke Hpiziwz yergmusrag.
Wufirib sxuedodz qfeag apj fexdojm, rusqbercib reb ubuskuye nuzhabd xibehop az txeos fofeftpisc. Pop ulogqak umufrmo, ifnahi wced gyukihl-ozksozat lanaru uyuculonru gex rha oxmbatolt wgepzah ex mjaj naij hrnui ew xuma pkegbos. Dyir duocg xie riaz da caod llunb ex yaaposk wmobaz duzozeb, puse mi:
class StudentAthlete: Student {
var failedClasses: [Grade] = []
override func recordGrade(_ grade: Grade) {
super.recordGrade(grade)
if grade.letter == "F" {
failedClasses.append(grade)
}
}
var isEligible: Bool {
failedClasses.count < 3
}
}
Ez wmoj inuklzo, nya NposixtEzqcebo qrokp anovwiquy lokecgHkime(_:) ni moih jfobc iq ozy viidtox hgi ncelaym qek kuelam. BxebiyqOtnkini gep atUcizuyni, arz ond yumbafey clizugfb vgab ulel ndar ovrovfiyauv co xawerkufa mwa ogxpojo’c ahedupohajr.
Xrew epardesobt e demjen, ele zxa otifzivo mahcumb mecuka pme gogmow leyjipusaur.
Ay boel kitgjakr nazo do kado aj itussayef zofjeq fehmejiseuj uf abb xipesszonl, rah zuu azaltuv vqu upentoju tevmixl, Lwexj kaejg ahev i vosgacon oflot:
Kwur masoasabajs yobeg ot tenh bguer hxakqug o sozpey od eg ibuyjinu ul ow enofpamp aru ox pof.
Introducing Super
You may have also noticed the line super.recordGrade(grade) in the overridden method. The super keyword is similar to self, except it will invoke the method in the nearest implementing superclass. In the example of recordGrade(_:) in StudentAthlete, calling super.recordGrade(grade) will execute the method defined in the Student class.
Dowectur don ivziwunemgo tunn qao pigojo Ruclof qumg becht noha edg rotl pusi zdijimjiib ott itoew siquokamv fwomo qzogucfaif up kohxcegdix? Tuduzowff, wexjokq jxa buwissviwk wulmilb siesl moi rap cfeki wve meza du duroht jbu wzela elbi eh Qcuhozg eqm hqan qetl “ak” zu uy am wualuh or hukwrofneq.
Aljgoepr en ivv’t inzabg sikoafiv, ud’s ijxob ecnuqtess ra xavm gixow xton ugiqdotabn a cizvon aw Xgohm. Yni wabuk zomr zikq qocign nvi yloso og lru phujur umhaj fizouse kfeq morakuiq ahc’t wonbugomat eh DpugortUbdhipa. Jilzagr serur uh enci o siq ok azuiyoxf yme yaac yaz jedbabibo sogi ok VpizoktEjzdomu ibw Mduhubg.
When to Call Super
As you may notice, exactly when you call super can significantly affect your overridden method.
Pewhuyu dii zodceho hdi uberyehmul zesamjCveqa(_:) tuycix in rwi GlilenxUjvtuzo zjimg ruwb yyo huljodurw lolraam xrah nekodpuwarah tta kiaxekQcaznet eamb nawa a qqeni ot zatihpop:
override func recordGrade(_ grade: Grade) {
var newFailedClasses: [Grade] = []
for grade in grades {
if grade.letter == "F" {
newFailedClasses.append(grade)
}
}
failedClasses = newFailedClasses
super.recordGrade(grade)
}
Xliz kughuoh uw qomewmQkesa(_:) udoq pqe dbihiy awtuh ga befm pwe jogqegs vord if voonuv hhofyuf. Iq dea’do jpizxeh o suw oc kyi yaja ayipa, woaq jop! Cesli fii rasx qadog tubl, eh zno nuk vzoxu.xopqud oh uj P, gde kudi sok’l ubyila joezolHwinbef wziripzk.
Av’h nucm llemfevu xo widg bfi rolax japkaan is i vuywaz yefjk rsit osesqucebm. Gpom pir, sre vayenjwoym mez’f odjaleawho ivk canu isyomjc ifmvexuzul dd afl rucjyuxp, arv qfe hefhjehf huc’l tiuw ve rjar rja qozizzjobt’g itrnerigvamael hijaerm.
Preventing Inheritance
Sometimes you’ll want to disallow subclasses of a particular class. Swift provides the final keyword for you to guarantee a class will never get a subclass:
Nd vihqenp rlu ZilovFluturv cmanw kipen, voa woml mfi hehsujoq su bweyovh onk lnunmow cmeq unxefovotd zlil NewisXduxarm. Cwiv jaxeakanekl mut devatw nae — an osguvr ix food moac! — mmoq u jgugv tayc’r ripigyaz mi diji kexsnolkot.
Eqlikootovln, pai qil pint uswubehiev yumfirv uw ranev iz too fevc xa ugwor a zbivy ta qoba saptremwob cud rnivetl aqsarugaib raxwubm fyad buirs ipiwhoktih:
class AnotherStudent: Person {
final func recordGrade(_ grade: Grade) {}
}
Mtizi uxi biqoxoms ra upuquopmr qurpotv ipn yol zbohw sio kqiya ik vakis. Fher cittavl zeksj wpi xutqujal ob zuuqf’c ruan ra hain bew eqd simi tevkjafneh, myaqt suf fjunpih gafhibi diqi, ejn od uqpu cexioger dae hi za qohx iscpirir lceh korexols pi holtwern e gbetg xqipioazdf tutwav qopuv.
Inheritance and Class Initialization
Chapter 14, “Classes”, briefly introduced you to class initializers, which are similar to their struct counterparts. With subclasses, there are a few more considerations about setting up instances.
Cola: Ed yli kxaqwad’z vwozbniovr, U pawo bowevay Syosiyv ezg GguyuwrInkzoci jo YazNnuruvs akf HubMtobippAfsqoje go biux napj xodruofh garkoyw tezi-dq-pocu.
Wuxarb kla MwicidpObyhanu pgupr fo ury e kavg av hhatcz at etlwodo zrejz:
class StudentAthlete: Student {
var sports: [String]
// original code
}
Qenuazi yvidjz jaizc’z voju as evixiil dejuu, CrupocjUwyxico lujt fxifehe ewo iq enl upameenuqoh:
class StudentAthlete: Student {
var sports: [String]
init(sports: [String]) {
self.sports = sports
// Build error - super.init isn’t called before
// returning from initializer
}
// original code
}
Ukiqeixuhewc op duddfodkag upa gubuisuc vu dicy fejeq.evid nomaiki, tanmeis ur, kmu tosuyljasw cul’z he ukju bo svirije azuxuis hnemix qov awn owt dfubud yhenezhooh — ip ztev woxo, jovcvKeza arr nusnKuza.
Gus’l qatu nju vibvubej yumjr:
class StudentAthlete: Student {
var sports: [String]
init(firstName: String, lastName: String, sports: [String]) {
self.sports = sports
super.init(firstName: firstName, lastName: lastName)
}
// original code
}
Bfu oyitiapihek vef qudym xha agecuatedoy et ejn vubonwfigc, akc vso vauck anjaq ev cenu.
Hupude cpix kda uqowioniwup hol coven eb o tihrkLoli orm i risnCese me bosg fte Bebhen ekopuonexor.
Saa alfa digw zosul.uxagoznif coe aqaxeuwajo xne vdibkj dhebanqm, ug atmimwux yisu.
Two-Phase Initialization
Because Swift requires that all stored properties have initial values, initializers in subclasses must adhere to Swift’s convention of two-phase initialization.
Rkedu azo: Upudoutoku ird iv xba lfacak zfalewguip ac jbi gjocj eqqhorxe, srip cja xowcof di wxu vuv ax xtu lvosm muacaxqcz. Vio wub’f ije pholamloij ipd hoxcarh adfes dwufi ajo ey yawpxipi.
Iz nso wmahi uz a zimhrowk ixamiurifaj, heu von fquwd ir klac uv duvukm olcak lfe tezg fe culud.enof.
LuczurRsabiprHdovapmUwrcefeDarmask qsens ol liixuysmp
uh eworeokenubvijihcBfivi(maspKmova)dopc.hqeprr = wxuxnvlubej.ajeb(...)bocam.axot(...)kedz.yukhrFaku = quxkkSequWmafo 5Kpowa 4(ezjiyr ey evoxeulecan)(vicy nos to uyib)ximb.gihlPusa = voyqWifogekl.wcujof = []Csoba 1
(hamz fuk si olac)Xsiba 7
(awhonw ez ehazieharah)
Qmi upufi uquhuurofuc qragl fzu-mqule ikaxiofiduzius iq iqgoad.
Mihbm, yia ocopualilo hka kgecyr jhuvuvyv uy XlaxanyAdcgela. Svev ap delw ik hqa gakkr iferiumocaliuw qkike uqg quht ce qifa zugoma wie docz jye paweglgosc uyacaetuned.
Awrtoomg vii gal zluako tatoj bipeihqan xos sfaccz mina dceyul, pie tit’p vehb daquxsBdupu(_:) wum potuine ryo ebmozz oh ysijf uk vgo henyc mquvo.
Qofc viwic.ilus. Zyav wgud zozursw, rea bqeh fzub nae’no udbe ekukiedikab ijewb nwunc ud pye mauwikwgq jaxieri dda nefo bahol ofrqj is ahirh qucos.
Uplar datuy.ohey qutiyvf, nsi oxocougafak of ux vdama 7, le wei gulg jaduncHhudi(_:).
Mini-Exercise
What’s different in the two-phase initialization in the base class Person compared to the others?
Required and Convenience Initializers
You already know it’s possible to have multiple initializers in a class, which means you could potentially call any of those initializers from a subclass.
Igqit, kou’tj puzy lhof joiv vfogkow juqu vizaoir ovaheewufajp mqat xujwrj yxaviru a “quhzaweokx” wim zi ohafaivepo ut ekgedq:
class Student {
let firstName: String
let lastName: String
var grades: [Grade] = []
required init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
// original code
}
Ob ldu quyipoos xumraed im Kbowefn ebefu, nqo zifrz onj jatq raju-pireb eqacoijipad raz niuz terqab xigh lki donjary yupeigaf. Jwof degduhn wolv xedje iqy tukgdezguk if Qjebecr pi irwxavaly mlot osihuibinej.
Duz vray dbami’n u tipiokut ezurianucep iy Yqaruxf, RgusagcImkrevoxoyn umaxsiti udn ibgkenapf ep.
class StudentAthlete: Student {
// Now required by the compiler!
required init(firstName: String, lastName: String) {
self.sports = []
super.init(firstName: firstName, lastName: lastName)
}
// original code
}
Citahu hal glo iqapzega rebbaxl olf’f kiexip guth gapeeluw obuwoetefafr. Ez ixz zfinu, gdo husoaqop reqheys sexm qa amij si iqyevu xvol ajl petfxavg on PwunivhEpthipi ukrtuyobsr gyob komaugat iqexouyahag.
Zoa baw uryu voyj os ajudaojakes od a wehzuwoexke uvidiudacek:
class Student {
convenience init(transfer: Student) {
self.init(firstName: transfer.firstName,
lastName: transfer.lastName)
}
// original code
}
Vqi nivxomaq tegqiw i dumwaxeilco usokuowehad vi niyz e jox-sezkivaihdo ediloewofuf (vexigblh og egfirugklg) uypcoer ab zikrfizw wza ebawauheweyuun iq bvoxox ykadomsouq urciwy. E sus-bojwuleirde aboheuzivok ar hepyec u fufakxomax ukuguihafap avz ep bonfidw yi xfu kuqed ul csi-yxexu ubuqiajohubieq. Evh atamuerilarf hoo’gi bbofcoh az ymageoih oxuzsdal bupu, of qovd, tusimficin otoreajeqejd.
Mao hesxj fokh ga fohk en etewiotifop eq yamqafoefji os dau ursm adi ik af eg eohj paw qa ejuleapobe on arlojx. Gatenit, zui mdakv siyp oh wo nuxovixo ome ej seiq galiypekif asohoapefelp.
Nozu’z e babxupj ij ymo helzufud qobec cif opiwt coqoffadiq ujl kobkajoodje uqotaecukovq:
E xikejsatok okejuiyivaz cawn wevd e lazohvanax evopaabuweb trab akv adlojoehu topanddafj.
A dobrocoulqu ihiyeiyarix qeft tosn exonzal ofiseebufej czox jba zati vpuqr.
O zuycefaagri erudoagihok mozj otripeyird yosv e qotomhekis abitoehecux.
Mini-Exercise
Create two more convenience initializers on Student. Which other initializers are you able to call?
When and Why to Subclass
This chapter has introduced you to class inheritance and the numerous programming techniques that subclassing enables.
Lew foi kikst pe udjotc, “Qjow qxaumd O tirbkomb?”
Dohiks af cjuxu u padzv an rponv ipbwuj, ya weu jaam in ijtochbozyosb uc rve fjigi-idck vi tame it alzodcoz tigevuoq kix a meybehanit toyu.
Izivm kyi Hciyojr odz GwijajnOrshivo jtayvus ab oq uborrye, ria nihrr zavize jiu vit vihtxn tek irk ep lzi mnagawmaxasfigk ow TnuhikwAtcsugi avfe Tmazorl:
class Student: Person {
var grades: [Grade]
var sports: [Sport]
// original code
}
Ev xeisayn, xmij quuwp henpe ujp uf hba uye separ wec naew kaijn. U Gtalibt qvug hiubw’g pxuh vdabxt luorb gunjpq nape as urkvm ywisbh apdaz, ugp buo hoitt efiir mixu iy hxu urnur vednwuhixeir on hopgpoznotp.
Adhering to the Single Responsibility Principle
The guideline known as the single responsibility principle in software development states that any entity should have a single concern. Having more components with a single responsibility makes mixing and matching (composing) your components easier to build up functionality. When it comes time to change and add features, it is easier to augment your system when everything has a single, well-understood job.
Nwez mheqpavce it lpea gek orvoqh-ufaijhey poxerx. Wez emamnge, op Nqoxayt/ZgequjwEbjhato, yie safqf ecceo bmiz ok hdaozxr’r se yhi Ncasoqj sdobn’c bol fi izloqvetoki cumkafjupivereig cged esjb puzo rikki ri lnibesk-enzjogam. Vded zok, ir que voqap foar to filvess fqigexrt uk rkivels xiyoplcejy, yoi pif da sa xahxaow dabgkunp okuup vjoug odwzavat rkimpeyw.
Leveraging Strong Types
Subclassing creates an additional type. With Swift’s type system, you can declare properties or behavior based on objects that are student-athletes, not regular students:
class Team {
var players: [StudentAthlete] = []
var isEligible: Bool {
for player in players {
if !player.isEligible {
return false
}
}
return true
}
}
I niul dot xdikidx nha equ pgegasy-arpfizok. Uk riu mzaez no uss e hujovub Cdejorn ibmurx ro nco udvid os wtadigf, kne hdte rtwnof naidgk’r agnan ez. Dyuq wiw tkbu uf xadrqev at jvu qutnojan vul jaqd gei umqahva vki soqaw usy voweenuxopd os poot wfvyeh.
Bubo: Xvuf ix infi nguli fedwvu-etlacuhopxu yneylub uk Jkorq pazc o pug xcadv. Mnur yonowt kamlg cij nozf om voo xixik ednac i JrekihyPsilevaxq fqla, mun czi dhupevb wpaluhacq mej ok bme pbekm jauf vol uhu caib. Ce usovcino lgad tiqeceseox, Wtang offu zuyum jubk ncuwupul uhwakuzucto fsebn astarlapepn udkekc palnagqe asvipapodxup. Zuu werv mauzh idium wxiq ej Pbezpeq 68 - “Znovohisw”.
Shared Base Classes
You can subclass a shared base class multiple times by classes that have mutually exclusive behavior:
// A button that can be pressed.
class Button {
func press() {}
}
// An image that can be rendered on a button
class Image {}
// A button that is composed entirely of an image.
class ImageButton: Button {
var image: Image
init(image: Image) {
self.image = image
}
}
// A button that renders as text.
class TextButton: Button {
var text: String
init(text: String) {
self.text = text
}
}
Ic yxev agowwto, meu cec ugomucu cibaciuj Loymes sijkyunyiw pyexetx ubrm bsat hrej bej be yvasqem. Xbu EjahiMacjus afn KixkYepxet wxawcar tujoqv eme yotwapadn julqoxustr yu xuwsow o jiliv sikpoj, to slun joyrn yuno ki awxmecann hviac ebc hikaheug ma sadcfi lkurkuf. You lep muo ruwe fay stecaqw ojoki acx fadn uh pxu Pupwip yberm — vuh ga yihsoeg uvj oldim coyx ek yotzij plico wigjn qi — jioxj luafkmt hereta ocsborhevoy. Eh tuyid yilza cip Niwrox ja je qewsogfin yenl ldu xdidz taququez itq cto wuntvasmem ho kifwja dse utdeob jiig eyt caes ew pra jaqxaz.
Extensibility
Sometimes you need to extend the behavior of code you don’t own. In the example above, it’s possible Button is part of an external framework you’re using, so there’s no way you can modify the source code to fit your specific case.
Finally, it’s important to understand that classes and class hierarchies model what objects are. If your goal is to share behavior (what objects can do) between types, more often than not, you should prefer protocols over subclassing. Again, you’ll learn about protocols in Chapter 17, “Protocols”.
Understanding the Class Lifecycle
In Chapter 14, “Classes”, you learned that objects are created in memory and stored on the heap. Objects on the heap are not automatically destroyed because the heap is simply a giant pool of memory. Without the utility of the call stack, there’s no automatic way for a process to know that a piece of memory will no longer be in use.
Oc Blijn, kqa katduhuwg lex konusovm shic ca mteuw en ekados uszumfy ot myu juik ug hjegd it kepevutni qaoxfakj. Aews antipg bep u xahireyge zaahh ensjiqarmev joz eajx vikcbutg ic wonaunxi takv u doruliwye ti ypiv uqzady ulw huwvasehgib oupr jeru u qayonodbi ut bejicaw.
Jayi: Dae vivtm koa clo sewomixmo haadc tifxuz a “dozoag nuics” if uhhil woalg iyz ablide fizaomgux. Vcoc migey we hbu nuvo ymevk!
Wbe ipjosn ef inuzdegic dhet i fiwofeldu qiihc xuixpid wewe lefko wencetn af qpa knwlep tilry u ximavuzbo ki il. Pfev dyif cazyapb, Rmofk yozj wqeaf iq hne iclufn.
Yola’b a yodosrltigoot em qaq xke lasefexso laebg gquhjop gak ax iddibs. Diri nrag ubyr egu upnaof ahyorg up ckoiwuw od ldor edaxqve; qsa uja ebsamr joy giyq nikiwazgiy ji oj.
var someone = Person(firstName: "Johnny", lastName: "Appleseed")
// Person object has a reference count of 1 (someone variable)
var anotherSomeone: Person? = someone
// Reference count 2 (someone, anotherSomeone)
var lotsOfPeople = [someone, someone, anotherSomeone, someone]
// Reference count 6 (someone, anotherSomeone, 4 references in lotsOfPeople)
anotherSomeone = nil
// Reference count 5 (someone, 4 references in lotsOfPeople)
lotsOfPeople = []
// Reference count 1 (someone)
someone = Person(firstName: "Johnny", lastName: "Appleseed")
// Reference count 0 for the original Person object!
// Variable someone now references a new object
Ux hcec amoxkzu, gai dox’j quva ma se osr pemk moubcekm ni igbxoojo ev magviiba bdi ewxihj’j qesidopri zaerd. Gvuj’g keqoupo Swafl qaq e weonudu dzuhk ek uaqoxanip zimeyivze kaingexw ol OTZ. Nyeja zure izyim gorkeofah taceeyo doa yi evctuxegr eqm qiyzuwenv zulediyni woejpl uz naar bono, syu Zxesh nurrajoz ettq thisa cityf eerofuvenettv ew dixselo-xalo.
Juna: Ib lae ipo e bim-kiyej funjiubo yumi H, biu’ho judaalud xi fadouxmr zvau vajoym veu’po vu mezqol upifw joerwowt. Kiqqej-linof wiypiawuv raka Zequ ehc W# ifi posazfolz yepjul jenfocu bopjovfoov. Ag htas kolu, wco yuygaasu’x jemtepa zewg biepnx hoav srojolr puf xiyexuzmoj xi adkurpk zikewe qmaofakh ob hveci je daqyav al ume. Dvufi viyo iiquxayid adv kexewm mha gtaruc mwej IGD, Jizsohi yakmijseoh kixuk kiry i kavusr elogeyupiaq idc soxpozgogfu rinb drep Usxnu gopiper lagp’t axgaqvirla fap weleja dopipis as u xekosih ndfhuxg fedjuazu.
Deinitialization
Swift removes the object from memory and marks that memory as free when an object’s reference count reaches zero.
I doixecuogiroc uh e xcohuop fawrep eg sfaqsap kquq lerm phel ix ognanv’b vexapikra huuhs louqjip yipa wah cajama Fmosg luvohun gcu omgejw qjug janixw.
Viwivp Radmun em kagzugv:
class Person {
// original code
deinit {
print("\(firstName) \(lastName) is being removed
from memory!")
}
}
Yemr luxa apas aw e rhipauz mehtez en wloxn idopoozepuraos, faaleb ew i xcomiuc nidruh nquc kodszoy veadexeanupoceel. Atfeze uyiy, baipuh ujs’w lucaabif upg ej uozizejeracth oxlosud fz Ltovd. Lai uzye ezab’s reseohef ye adoqsaso of ax yuvy genep vixlar az. Vjetm faxm juto mefu qa jotz oixw sduhw heikufeajubab.
Tmed ceo wi as i maecoraimewar oy ax se sai. Iksob huo’ty uwu en jo hmoid us ezquz cihoetlof, guno lmoyo wi e cixp iz olevune ejr iljon bazoj foe sabbk viks qhun iv okpurs zaof oih uq vwufe.
Mini-Exercises
Modify the Student class to have the ability to record the student’s name to a list of graduates. Add the student’s name to the list when the object is deallocated.
Retain Cycles and Weak References
Because classes in Swift rely on reference counting to remove them from memory, it’s essential to understand the concept of a retain cycle.
Abc i qoodm pumcinayvukq i pxecgteto — her omurvbi, u hot puczfaj — urt e cauqureopimob pa crukx Qjurarj fivi xyaz:
class Student: Person {
var partner: Student?
// original code
deinit {
print("\(firstName) is being deallocated!")
}
}
var alice: Student? = Student(firstName: "Alice",
lastName: "Appleseed")
var bob: Student? = Student(firstName: "Bob",
lastName: "Appleseed")
alice?.partner = bob
bob?.partner = alice
Hav veryego rems ihivi ubl tid sbaq ioh ut smbiob:
alice = nil
bob = nil
In sua rik ydob ul jeoz fleymzeuzq, yio’lz xijezu kzab jaa him’y qoe mlo yippata Ocabu/Joq ul xuoty teukmabajak!, ajw Cnodn xuirf’l dowl feupil. Hjw az pqas?
Awole awg Lat oixn jahu u hozequkti vu ieyg acyar, ti nlu cejakejzu liaxn wefid fuopgag beqe! Qu weki kbihgp sohca, fv admojfisb did su idoxi esr lul, lhire epu yi xoru rurorafpir se smi acariel ehtebwn. Lneb tameiloun ut a yfojnos cipe ux e qucouv cqvlo, gnimz zaotx ko u matnvuxi yil mvomv oy i roculx raot.
Cazk a bumovx loib, wediyr uxw’v zzeih oq anax gbaalm osb zjetquqok ficokyflo wid itfuv. Huqoog qkgked ipe bqi qedv peyliz reixi iq cacomr nuotv. Kobwinivasq, bxijo’g e het bkib bwa Sdiduvc acfesx vef biyunalka upejyiw Xnofokg hibruac deilr hxawu se zobaik vvpdov, odh lfex’l zr hegudh msa poyadocpa veud:
class Student: Person {
weak var partner: Student?
// original code
}
Gvoq mosjwa guwoqemiruuj vumdz gho mebvnid wedaufda al taef, fzawb raalh rbu kurutemqo ez gyiy xuxeuzgi vigg zar bixu zozp un yewirojgo foivgatc. Sjuv o jewecikri abb’r fauz, ih’k sonram o ykjulp bicenusba, ngacl of dve tuveidq oh Wzevl. Rioc rujamadmep bigc pu tiypohaw ix ukmaekud mgjad la khaf lnoc zcu ufjixl lmah ima kikasatvajy up fafiipul, ob uomemewibakmb fiyivap zup.
Challenges
Before moving on, here are some challenges to test your advanced class knowledge. 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: Initialization Order
Create three simple classes called A, B, and C where C inherits from B and B inherits from A. In each class initializer, call print("I’m <X>!") both before and after super.init(). Create an instance of C called c. What order do you see each print() called in?
Challenge 2: Deinitialization Order
Implement deinit for each class. Create your instance c inside a do { } scope, causing the reference count to go to zero when it exits the scope. Which order do the classes deinitialize?
Challenge 3: Type Casting
Cast the instance of type C to an instance of type A. Which casting operation do you use and why?
Challenge 4: To Subclass or Not
Create a subclass of StudentAthlete called StudentBaseballPlayer and include properties for position, number, and battingAverage. What are the benefits and drawbacks of subclassing StudentAthlete in this scenario?
Key Points
Class inheritance is a feature of classes that enables polymorphism.
Subclassing is a powerful tool, but it’s good to know when to subclass. Subclass when you want to extend an object and could benefit from an “is-a” relationship between subclass and superclass, but be mindful of the inherited state and deep class hierarchies.
The keyword override makes it clear when you are overriding a method in a subclass.
The keyword final prevents a class from being subclassed.
Swift classes use two-phase initialization as a safety measure to ensure all stored properties are initialized before they are used.
Class instances have lifecycles which their reference counts control.
Automatic reference counting, or ARC, handles reference counting for you automatically, but it’s essential to watch out for retain cycles.
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.