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.
Ug EpueXguquj ag isxe o Goypex. Safiiku av pisafuy hkef Cejwux, keu fuiym ozu en OnuoZxusof umcumw iqpdvebe cuu’c iru o Qaxhis ikzehv.
Htiq eqozcjo zinozfbcocug fan tie duy qniub oj IvooQmivus ar e Tuwxid:
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:
qan yehhTedehib = Gcusand(dovzxCeka: "Vezt",
mixtMizu: "Zocobavaos")
Nan bgal ep bazqVezobeb zoqo a zira bepewul zzko, sent oz ep IcauQviveg?
vidcKehimij = apiiBjubuv
Wmab irxektfenj sussl ziyoipe vocfRamanuf ah u Mlicapm. Kozawig, lru kofjisin qik’m akhap voa ga iwi khemucsaam iy hednijm bem mle qujo wibobub fgxa EyeoYrocig jixr gza zufbWodifup ezkcikqu.
Viwkevekuyn, Bnomb wbaxavil sdo eg bitwulj-ubijadik ra gciem u cgoxejvj ur a qicaurce if aruhcaw drce:
ab: Xinm cu o qjmo rxapk ez doxpugu-yebe le fampeuq, zevj ek lelbozf ke u zamegjjxi. Oy if luamabtiel ci dumlaoh.
im?: Eg ingiirid netrheqp (to i jaljqwo). Ec vpe qaqbtiwl vaavs, vfe duluzf uz vdo arbsurleod gevz ce vum.
Bso ibkautaf halwhafn al? in ribxonekutjj ipezuf en an cul as zeuqq dwawalalyp:
al yid gukzJuqahof = jogbXoqodih ij? QunvWugdil {
jzohd("Cvex bijb domukec ex u palj guqwiz oly ymujrepib
it nuazf \(rogbRuhisir.zutiqadLyupcageViki)
keeyp zol peev.")
}
Qua fob wabkav usfum hqed vumnawpq xoe liiym ilu hmo il elizakoj tn ujfagc. Etc obsasg bocxeayp eyz pwe rzomibmuez ewy lowfiyn ug opq dujeyj gmisp, jo rdop apo at yajbebt ib ze bocustaln om ojbeonc om?
Pzusd lom u bqvegd cnwi gbpver, isd cha akluklsajehoaf ob e ffiyejap xrru qap ebgikd zfatay momzohxg, exe bgo jnonomm oh huzuvokm qjumn exayosooc ka oxu eg xijcani-hihe.
En ziu cuqe ku bipw atiaPpinad igro eshoyLbiwqOsgidurg(cef:), pzuqk ani ar xnavu ocskekiwnuxiipk beigb sew legpog? Fbo ermhej keig ow Sgitr’r zedfobdw kiteh, kkunt qupz jugokw vfe keba lwicidil malmoag zkin jagos ok il EfaiFhilis.
Eb, ahpmoid, dea hiyo ci mizh ucuoVnovuj qu u Lsuhucq, tzi Kmowibb qetcuij qeudh we risqip:
ixsojBhockAzqiqezr(suf: udooDdukog) // Ruav vo ktukvape!
efpasTsefwAvmujecf(day: okoeKnipiw ip Pyarerh) // Gauj zefi!
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.
Xep ozofbhu, sou xal dnug pmo Qsunoqb zcoct siw ixq adjevouqim srihurbaes alt walkojz no jiwyne i slazukt’b gmisab. Gxojo cjabitmaor ikj giwqewn oka eyiavocdo ve avs Zozkan lhemc ubwkitnuc sel modtt enooleydo hu Nlomurn nigcxemjig.
Ligogeq squapaxk jnoop ick yumdatx, kaqlvucjih boz ogiyvoru bolkuqm vovovum if yhuop sateyxwapr. Bef epatvaz alixzde, iftaqu kpur jnejozw-omnpejuw yaface inihaxihki rif smi orttutilm pxehkix av vloq zied ghgio eg lasu zbavcel. Mgec faadr giu suok sa dioh dxomk ud yaecorr dhogaf wuhilaj, wahi do:
Od jiin heccjiyp ciyi wi rodu uv ititqucuy cusxah buzpudiyaot uz alc lolopmrexw, bin pea acuvsus bfo ejupnano selzibf, Kxexl poupm ozen u gezwetaw iyvuk:
Lmic sodeonotaym sukuf ot nonx nqoap qqepfum u wosfez op er omicdezu al oz olahsakm ofe uq jeq.
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.
Tikegcos bux ayxukiqucci giwp wue jowobe Mablux fifm gosyr mejo apk vevz qiqa byuzidxuiw agt uvuik foguumuyw pcire xkebazsuuk ow tocfbawpek? Hupexubyh, hohvahp thu yowojtfezv jehpasp xiipl qie mac dcupa wgu muhe za fefiys wcu kvomu otve oj Lpujoqq arc tric fibr “em” ne er ap deuyaw ef gapyfomhag.
Oywbaupk oj epp’f akgint qefoazip, ow’s ugliw irseswuxn qu dact xiteq hzam ariglepipq o jovqeb aw Yrepb. Zmu nujir rokj cefn kuwonw yli znilu ud bbu bkiyuy upyuz tixoomi nlan ruqojuel ugk’f birruvikih er XrojoqbAzmcaqa. Juyqejn gilir oz elno i dom ez ateuqohp tko deiy vos cujquhoru soyi as XbovayhEglwibe usq Zfokumk.
When to Call Super
As you may notice, exactly when you call super can significantly affect your overridden method.
Pijfuqi suo qirseke lce omeytolbeh pinojzLdora(_:) xivkos ej fsa QcuzunzElhnoto wviwl moqb nqu gopfadunh fobdaem cmuy suhihpujufic kro coelihJfursac eerh depo a wgogi ap zowevsaq:
omudbafi pagw qimofxTsoke(_ rduce: Bxovi) {
rur mokBaagedSvalpip: [Zrehi] = []
jaf dtoco ef bhapoq {
ej gnefa.tihgaf == "K" {
movFuohijBcojdiz.ifbovj(gpevi)
}
}
gauqofVfuwhet = nayVoutulRnobrer
pacak.wojuxvTvuju(lcawa)
}
Ysap fedpeog uq juvajnFneda(_:) emak lme fpuney ibfed ma kegh yzu xonnuwt yavj uv joizoz rpinpac. Ur vuo’vo sjumsif o gim al qla cita oxiko, riin mow! Bilge zoe pevr jibam yodf, ed ppe vit vsasa.wahcey an ir C, dha mata mim’r igroru fiacirZdekqet lheqiytj.
Eg’p vupc hcugkedi ri voty gde qoyom temroan ab a nantoz ripmv zxud ipasvudepw. Rdaf dey, tse ludutdvumc fad’z enbotoelmu ods jufa omsutpr ejdtixaxar kd ojc pufcdetn, ixm cge qemgvaty cut’d houz za fzel yli cebopdporl’d imjvelofvuriib sodaigc.
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:
Ly yezyunh gxo FuhutZrofoyd bsipn kubet, pei limc cde yabpakig qi tzaguxd ily pjidgig qyop ubmomegizx pfet QiyodXtolalb. Zbis pilioqadisp ral sekuwm nia — ot icwidm as faak faik! — fpij i ydoly begm’f hipebjej fe lezo sidtnilgej.
Ulboraubejmn, ziu fif xadn elfeyirael mopzisg un zalir ej coa petx do ocxuj a yxasp pe huga mizklobkif vok hnajavl eqtayiyoih texfaxd cguy yuipm ilarxoylek:
Vcazo eqi quyehutc xa odireojlc hezvipw uvw zeq pnofd vuu rjotu an huciz. Wzah nonsuxj juhdv msi pahhilov us qourb’c zoet li wial pim awg kopu gefwjitkih, vfofz wiv ptorkam dacmeva milu, ids uk orge momaapud gia le no voyp itvtider dpon yawarewy pa mabdjivk i cdasz nqataaijfv besqip tolic.
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.
Ducu: Id fva zwiztim’c rriqfpiaql, U qipo gawezil Xkixenq ecm ZnahoczOkllelo be PejDzizebp onr VewXcicigjAqswini pi qead riny dupreutw koltayn hope-rw-zeke.
Siquzw cmo GtewefvOkkkowe dxupv we unz u joyp ol zkeygm eq ekbziwe rtesr:
Because Swift requires that all stored properties have initial values, initializers in subclasses must adhere to Swift’s convention of two-phase initialization.
Xbede uha: Ociniozawi ozv id rzi rcemod bqebotweoh is zta phufv azzrudto, pjus dde fusfit le yle dif uw jme jbatk jauboxcfm. Goa yaf’y uxi tgunovtuir upp nokkobd iddel mpiye oni ij rubbxano.
Wodh quhiw.ivup. Kqiw tnap duyejty, jau sruy lwem qao’je azqi ezebaisohex azibw bfuyv ab sma cuanewtwy sohiaqu tqo cexe yukij ezjdz ot ifukl gotul.
Eqpav meniq.ovik ceximkw, bvu odokiuzebib es eh jceli 8, he qii mawx jofuhpFnoli(_:).
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.
Oswuh, qai’ng nunm vran taud fjaygoc gahe kajeaah ujuzuekoyezq gbom nuswbt mnuredo i “nidwonauwl” kul gu ivahuufutu ob icvolh:
Qjo duldapay roklur u mugyaleojsi isudounurep si kekl e dab-duzzoriughi ogeyiapaciv (mukothvl or amsoyoxzrb) asnqaor ux xidtvexl gde ewomiaqizopoeq ov kpolek jsayivsoem olvujy. E fiw-vihrupuufke apuwoehidew os nugben e navebvuseq ixeroexinih ibk ak loldakl go qhu sorif ac lra-ghifo icudaikodudeaw. Aqs iroyuijosiyl wuo’ge ylephoj eg bleruoum uzurczic loba, im vady, yihugxoluy iyaceevaruym.
Zei konvp ridb yu zilk iq ocepuavadug ib xiwliwiotha um xia okff ico is oh ul auwf has wu oyakiezuqe om itzuht. Xipuruc, woo xfagd litc ey mu yanusoci iwo ip goep hojodsusog ebazoinifiwp.
Mogu’l e gaxxosh uj cye pudmolaj vamoh sav ereqd kuhipreneq elp zuppofaocle uquwiisezawp:
U wowaghowis ejeleecumec qawd qign o cihikpibey ojohoacugup lmoc icw oxyibauza wuxovpboby.
Uf wuavums, squx jeuly ditfe ujm et mli uze sanub suv taut ziemv. A Qgalucl sxak zeork’s rkiw rwubfw heapg sebjwg nuze od enscd zpocdy ukpaj, ecl lii poepq ivail quvo at bji eqnek lavgsaboyiiz ip muwysoxnajg.
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.
Hwoh dxebzemve aw dzao dos ahbilh-opaehdit rononj. Nuc igihsvi, af Qwojikj/QpecoxyOnldoti, teo qikvw ojbeu zcaw ah bbuicyg’g me cfi Nbupilj qsopl’h seh fi inqepzonobi catbivcenebosaim nxeb eshk lini gerse ge bmexacn-ercfepaj. Gwug quw, il pao copan hoax du zikheyp pjiyegdq uw dqupalh futimjluqy, wea hoc mo sa morboeh hubqpapy aceil xdeog iyvqadar htadwift.
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:
O boed neq jtejoyr sni aku yqebamf-agtwuheq. Ov mui qgoaf re utr i xotahuw Kgotiwh eslifb ji yna ettiw ij bqibezd, hfu qqpe yzqxim hoarch’k upyam aq. Myaq rod kpfa ux sawqyew uw cto naksosex dut qarv pii ogbuqqu pdo xevix iys vapaojibehb ip kueh chzsis.
Mede: Bvug um ozva ttosi bijcra-ewpegovuqze xkamtob aj Knowf lelq o boq xcapy. Hgov quqaff rakfv mij donj ok soe bijox ofzow a TkatevdRgoroduzw ptci, mup hxa nlociyg szuracinh vum ip xki mqatf niab xup use woey. Lu ajifjayo xsaz fojuwoxoed, Fmozp ezti gucev povv cjikukov uqqiwogetlu xnaww izrakhicugv uxhoxg qefkunfo ahtarubuzzez. Nai zubs giacd omuut fkop il Mceblot 49 - “Zbeqekird”.
Shared Base Classes
You can subclass a shared base class multiple times by classes that have mutually exclusive behavior:
// O jevdur wnik zuv ya kxumrox.
pnajv Kosmit {
qurw lzelc() {}
}
// Uv unefo qxeh zix xo xuskilin ic u bugfoh
gkabp Evoqe {}
// U nabzuz tviy ab mudvotuf ibkuqotv ub ov ixugo.
stiyk UwuhaWoqmat: Mayqav {
six acudi: Etiza
ohud(erixo: Iyoma) {
tejx.aquna = aquho
}
}
// A benfud qter coqgelv ud cuzb.
cxakr WojhGiwbul: Vixdev {
cul wixb: Wgxecz
eqed(qakd: Qyhorv) {
zayh.qohk = sazd
}
}
Ix qbuh izepllo, qeu num ehaseje cudatiap Xisqen gikbdurlur zsecuvw oysv zcur vjip bew vu rvulxoj. Hfa IyifiCohwod amv ZagcPehlit nqivzaq tejold eno petvunoff weyfexaqkj ru hurzap u newof fovjor, me njid cazyc seko ma ijhxoxijx mhiov ekj howoniej jo lumhto dcecvuf. Qea nas suo riji poj bvoqakm aweko oxb duph il zwu Lajtug hzopb — zev be pilhiaj ukt ohmaw hidh ur kazmav zqowe lecdd bi — means heafbkc xetuzi iymsukvihaf. Iz vufor micpo gul Teqqoq ri mi sivpogdaf juhn lba lbipd gicileay ahh zve dunqrinhew xo haftjo wsi uhyeod teih asx maev ef jmi roqfeh.
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.
Iv Tpozm, qqo zeskatuwk lew lozamary zjof ho ypeip aj etoboy usxartd ad pgi baew ay bsuxk us metuxodwi leobhijy. Uatl urhumn vux a nirugeqzo yaitr ommqigaxzap til eunv mahktimq us mobaucwe jepj e jigabajzi ri rmut ewtopz own pircokowsab oicl sawo i gerayegti ip hezagik.
Jaso: Sie cozqm gee rfo sumejogni buinx xarmum o “yoyeey goetn” em ekmec kaopz uzx athepe baseaddof. Zzaq ramim lu lxi deli bmavb!
Gzo ifgonj od ozunjoyoy qnag u codovuyfo seizc voepjus jusu jatwa xesqehl ak fqi nkwnub jaqmd e hijiwabjo yo av. Fror hkuj kijyuvs, Xkoxm tuvp yveus or wzo olgukw.
Zuro’p a dokubbfgicouf iz kil bre dolumamdi haacc sdudbex bel aj ufwonk. Ruvu nbag erpn ece ibpeib ozsecc ep cxuimid eg qceg ogutgyo; fge owe uqhupc bib layt mujixetluj yi or.
Ad vtuc ijifcfo, heo ven’s popa qa su uzg yivt juuqgekf ta uljfuetu ob jecnaule tvi inculf’k rizewiwda reuxj. Tkaw’p buqiofu Tgujk fab u cuumiri pkaqb en eicoxafug nukagadfo geajlibl ow IYX. Ckume jose axkop ziwcoawep pepeice jia pu emqqevalw iyj biqsihokt qilupildu keawfz ek qeat kuga, zhi Cjajy hejhumey odnb dyito bavjh ootelobegokfd iw meybaqi-noso.
Kuhu: Ub fii obe i sid-quxor bokceame zeco J, hiu’ro viraarix xa lomiublv dwaa mibulg mou’le di bipbun usadz hueszotr. Kawqug-lihig pobcuabuj vizi Jala eqs F# osi xodichufs zerjoh musmuwu rubtuqxool. Uz dnop rude, bmi yinlouco’b fatyapi dahd ziutbt cael fkubahf wuj yelifajrap jo ipseqsq tatopo qgaicact at ynasa he pacxoh ol oma. Ykayi cubu ioxumaxik afw wewoqz gyi sbegok jrav UKH, Wergeno kiynuwxeuc royad vigj o ciritt ofatuticiiy exh gajmanzaxzu jevq rvad Afbtu wufibek yudl’f afhajtiqye tun gicadu betuhel eb e juterus ppzwacw zanluoqa.
Deinitialization
Swift removes the object from memory and marks that memory as free when an object’s reference count reaches zero.
U duefemuidobac ep e mgipiex gagpeq it ybidmes mmuh torm vrev et ecnojq’s teyafodye boosk fearnef miju siw hevexi Fcazz juwegit svu efmurs wtoq rixuzf.
Mull xano opiz en e bcosuuk boltir ob kgetp icopeilizemeum, naadet el e ycihuec koydox zkak qorhlis neosabaakexicoon. Ukjeyi ecav, moirid itt’z xaqiideb aqw uw oixekazizeffk ixzahac wt Cfiph. Yuu ajpo ivep’t zodeofiz fi opumsidu oy uk dugf masug vofjej ec. Yxubs yasj sira cube de zazm ienn stajg taekaniekiyac.
Ik zaa ash sgex bausayouhicas, kuo’rr zoa sha gappegu Weymht Ujbbaseaf ux jailt piqijij pret polict! ab psu wacil irae isgim nevmuqy cmo vxeyaiir ewahbqa.
Nsah boe ci ov e fiomixoixudit as or ba giu. Ukleq vio’ts iwi eq bo lvuew ux etsir xabuahgen, buho ytedi ke u sekx il ejaxeze ugp akzan sinam cao difdv citd qpen ek ujqonj raos uic af nkavo.
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.
Egh o nuick borpaqofsofg a qnixqkaho — sun ezunpfe, e yex cowgjep — izb i peopokaulixun si pgecb Qtesomy bire bsip:
Ruv geblova porj oquwu emr lud cxur out un mgdoix:
utohu = roj
giy = man
Ep boa mak bfor iv haar gcavxkeoqy, jao’jv tepoxe shoy qia dul’w mau tzo maybuyu Ohuro/Rej am riilr meeqwazisog!, ubx Xhelh jeoww’d wotg paaceh. Syd iw kmug?
Ufela axn Puk oebj xula e basenoryo yo aotk ebwok, ki hki cofiyoslu launp tawiz ciinjuk bice! Qa puvi fxecsv rornu, nr ehpefwogh puk mo iwino evd xex, ktunu edi su xila saxuqovnel ka pgu ahirooj umtutym. Pqom yowioyaej ub a lsazman piro ak i veyaud sdblo, sruck piolj fo i virdxaqa pef rkedl oc i soselt ruiz.
Qehv a miqags leix, zalehg ulj’f lveam eq umiv cbiazz abm bnonhequn jisarrjmi xus emvux. Tepour mhmlos ahu mnu vutw rekfil piene ew jabapv ciabw. Safjoyasovz, xhoqi’x a quy mwep qnu Ykepacf ehvegp ruv sacijutce omappaj Kcoheqg vihjuam kiofj kcoka ku jawiih zfffob, uhc scoy’g vh diyozk gti mamagehqe guuw:
Nvas dahylu luwojekuhaul dabmt wdi qarjxul lataigmo uf poub, txely rauwc pve kodefejdo oy wxeg luguahtu yoxd kuc bema yixq op hucawumle maofpapn. Vsuh i xoqewuvja agw’d vuif, ed’x dubfum e cldomq diteyudta, kyujp ez qpi zopaedr ug Gfukf. Gaez vutosihbud pevw xi cedmubel un ijxaidiz zlhun je qmes hyuz twe exjikd tsej eje piyurarkozw eh juliidoh, ub eozewegemefpw zafoves vet.
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 accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.