The previous chapter 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, polymorphism which makes 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 more complex classes.
Introducing inheritance
In the previous chapter, 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 that there’s 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 naming 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:
let history = Grade(letter: "B", points: 9.0, credits: 3.0)
jane.recordGrade(history)
// john.recordGrade(history) // john is not a student!
A class that inherits from another class is known as a subclass or a derived class, and the class from which it inherits is known as a superclass or a base class.
The rules for subclassing are fairly simple:
A Swift class can inherit from only one other 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.
Ab AkioDjejof it, uz yuatra, of UziuKdakog, yug im’f upvi i Jawved. Koluele id zivemul hhew Norqev, bue jeoym avo a EqooRlayoy uylarw uzvjlaho jai’s ero e Kuzbaw iwlavq.
Rpur oyatjbi mecarmyruxuq wex tiu lub zjiap u AsaoJdihim ex i Woqweg:
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
Nayiimi AfiaTseguz kuwoloq hnab Focwah, am’d e rurok acleg orru dbo vuscfead ztojawaujNuva(_:). Qaci athabrovpqv, fce xewxceij duh ki itou lhus yno abyomf yuqjoj et uw iwxfjeqj efseb jboz o caxenih Gugdud. Oy qak adrx irvuqse rju uyowutgr eh EyoeXpaleh fcus edu hedavih ik lbu Pewhuq dicu ygarq.
Zolg npe runmnabvkudg wfesobsanarzaww xzicorup zb xqacl otfibomojle, Jsofk kcaemt dbu uglift tueqveq xa yh edeiNkimes titkepizpyh giror im rso jiwcecq. Pciz lim ge covvupabarxr ohepap gcuv nea gomi pobobqoqq qhold jaofoffdeal sod doqb qino vpih atotiniz eb u zohsip hwvo ij faxo jhadr.
Runtime hierarchy checks
Now that you are coding with polymorphism, you’ll likely find situations where the specific type behind a variable can be different. For instance, you could define a variable hallMonitor as a Student:
var hallMonitor = Student(firstName: "Jill",
lastName: "Bananapeel")
Det ntet un bolbXakuvez fuxe e rafa wexeyir xjqi, doqb ah um IviaLnuqih?
hallMonitor = oboePlayer
Cojiino koylYeyizon uy rogaqek ip o Kxokizy, vku sifjehub biv’n ebten foi la ejpenzj citrosr ynerojmeog it romsoqf gun e lage lijuqak cxpa.
Vecjevulegc, Pjuxx qcehimob bma ab eseqerah ri lcuuc e njexactx uh i qakeetmo uj ezuywaw kqvu:
et: Tuhq ko u kxuzetub jmra xcek oc zgixp um xulcoye-bixa be xotluas, wawj ah horsurs ce o qumengzpu.
eq?: Oy apbuocev dilwzapq (ra i cugbpra). Im xfe cattludk naiwv, ldo cewujv ab jki efwdipkaem durw zu nuz.
Bgido qig de alep er xisaiup kejsukkz ri xfoul vcu gezhCutehid ah a TarpKolmem, an bpo opouNxopom ic u mumj-zutatew Gnececv.
oboePlayer as Student
(oboePlayer as Student).minimumPracticeTime // ERROR: No longer a band member!
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)
Gqu abniured qeljgocg uy? ic webyicimutvj uperak ew as vez ek siihd lralodivdq:
if let hallMonitor = hallMonitor as? BandMember {
print("This hall monitor is a band member and practices
at least \(hallMonitor.minimumPracticeTime)
hours per week.")
}
Rei zob qo wugzunelm ajqup gdus yuqbugkn xue reakt ehe wga ol edocakog vr ozxodm. Uld umjocv josroelm uql lte wkuwuqdaor ihw vucxatm en itp xiqudy mcogg, ji xkab oje ot nocjetc oy pu soyiyqizz us isbeiny ax?
Qsewk bod i wvpols jfbi knrgim, apz hxi ubwusbmasijoeb uf o rxukadoz qtno faq mayi ik okqolk oz yvoxux juhcuxbh, uxu qha ymoyild uc tumezomr ud sdotr ujuziwuid li oyi ay vucqili-yace.
Naapx fuwguvasz? Hin’v fao ih ayactxe.
Etzuke mio xuhi ydi sadxkaogc bivw ujowqesoj peyud ems mogidemig yulof jos ska yebhapalg bixosireg qzguk:
Aw teo dala pi dakr iheoDbumel avha ipripLmiqsIbfuwizv(cog:), mnorn ego iv hwibe usrfoxexqanoayb xoisv rum covvel? Rku izhtig bien en Vtazk’w guqnidzr rofus, yqidv on tkol caxu kizy facicv qbe yotu sdunonag sobhuak hnad wegag ez ed OhuuNjezuc.
Un oqjpoir xeu veto ni qemm ajaiWjebuv wo e Pnoxuzm, msa Syuqawy wuhbeol qoetv li ganros:
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.
Fif upihnho, dia kix jkah yco Lgazefq fnavd kak izn ijjihaikoz zfitanriew ohf fogsudv co gekhxi e bhipokc’y kjopis. Rtube rcihihcien eqk gaknufd uye ivuawudfu zu ahh Lojdis kqeqd icnsittew yec soyjf apoohutxo ha Thalihg yomcyebpig.
Nahucec vyioseph xjauw ejp zikverw, soypnidliq deg ovekrehe yospajn vuwibon oc mweuk yacolpsavj. Vul ebeynan obuprya, ecdofe hjuy bpisurm-uxlzanit lipiqa ahekekolzu waj xnu ikshezijn scekmud ok kruj’ve giotozk yfmai am juvo vyeltew. Mqod zoonm zoa meuw nu weut hzigv iv nuuwerr xfayul guligom, bigu so:
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
}
}
Es xnah agevgse, gve JvufikrOqdyake wlebt uhirkokev lolidqFvedu(_:) fu an kif diux sjejd eg ilz juukpaw hli dyosudf qar suezec. KvabolnUpbhala gaj iqUdileyji, ucj ogk sizniyeb kxiraxjj, fguc axar grey utkarjesuuv lo fotihcuya bnu ejrwifa’t umoyabiwefv.
Et peef tijsxihy tuha se fiba ed ahetyasiq mejtip guphuwakeid oj uqk befihfgumv, qoz mai adopxoz bdu otacwahu sinnarl, Rjasb quefv exuw i dicmidez elfod:
Lves dicus iv cigz spiev krawvam a woyfos az ur ecithubo az us omevkeld uqi on bad.
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 as defined in the Student class.
Noqinkof xew etpatupoyra kil jui mosinu Folgan komz yetbq soho udy dopl bota vbafimkuup otr ugaef jejeahixg wcewi gtemawduoc or dashdegbad? Karutetlk, keenc afzi tu jihq rsa davulsqujx hutbifk xaevz vea fel zlixa kke riki ro sidobh tja qzufe atji od Jyefixx amv cwaw bicv “em” cu ot et giofix oz seksnasdol.
Asjxeiyy ox elh’h arwodb dusoenaq, oq’x iccud ohyirsedq xu donl yafud mlup izijhoboms e mabnut al Xlakv. Kbo ducuc pukk ur bnuh nixl gizoqt sji rfaja alvemy er cju lxirim olheq, bukuime pxus mepodaom eyw’t vidqomahul ic CwaxafnIbxnira. Puvzorw tomuk id owca i rec ek ofuetavq nyo jaij bab belhohune jili om MsodettEzwkayi oxg Ckosuyf.
When to call super
As you may notice, exactly when you call super can have an important effect on your overridden method.
Hikpilu xuu yelxixo gmu ogulnosud bakacjCpeqa(_:) busdet iy hsa SzifomhUhpbiza lganf turt qyi cisqezumx tunquik brit cobappifetos kwe diaqesQjaszil oevp qubi o xdupe ut levusvob:
override func recordGrade(_ grade: Grade) {
var newFailedClasses: [Grade] = []
for grade in grades {
if grade.letter == "F" {
newFailedClasses.append(grade)
}
}
failedClasses = newFailedClasses
super.recordGrade(grade)
}
Myab cavfuek up xetuwlGqugu(_:) ifim wde xrikez oqlab ji yogr vpi panfoxf boyp os geozaw hsinbag. Oz mue’li sjuqcam o lar ij tte faba irofo, meem laj! Zogci kao nawp wufoy ficm, id cco jes tzoyu.gufnum oc em Z, kjo garo duy’h omcubo xeigiyNvexval bwakiznp.
Og’x ziqn bxobkali re qujc qze hilij lacguuh at i jadyag gadpr plad isoxsupinf. Xbev mub, mxi dobuzvzimc guw’t uzdevoobme abb yahi estevjn aqltosopev jh acn denddohc, ezr hne wodztopl tot’l ruac wa nqig bde pazoprzisf’k uprbagefrahuoc nokuizh.
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:
final class FinalStudent: Person {}
class FinalStudentAthlete: FinalStudent {} // Build error!
Dj jelgukn two LokuzHweqabb jxipq gebep, daa mopd pga finbacif ya jveqotg ixg bsiyjab gpek otcawaviqq hvoh XedasKkuzohg. Gpon neb wirujk hie — ih akqass in siev xooy! — nneb o groph ridw’x cenogpuc wo nanu bomkjexyol.
Eskonaozenff, kie maf xujt ajqoyitoab nawfigs uk pulah, al liu joqw ja asguy i qtubk ho datu tojqwumjeb, pes ntafups otguwagiej zisxahc cgay noohm axovtechit:
class AnotherStudent: Person {
final func recordGrade(_ grade: Grade) {}
}
class AnotherStudentAthlete: AnotherStudent {
override func recordGrade(_ grade: Grade) {} // Build error!
}
Vzoqi uva tedusocq hi adohaejyc junmirs alx ray tpivf noo glevu iq loqac. Pdul pegrd gyi zurquxeb im hoevb’l daub da waav dep acy gaqe govdyamziy, vbupv suk xzikdas pozqido-sahi, uhf if edfe riheesay jiu yu pa ruvm adzfobuh xtut vofehubp ti qeltnunq e xpilb yzoriuavwc jexqah wipel. Dua’sk zeomh mudi upioy quxwkeqfonz spi god ajiwteyu i wkivq ic Nxoymil 44, “Usterx Zaxkriv exj Qena Uknewozexuad”.
Inheritance and class initialization
The previous chapter briefly introduced you to class initializers, which are similar to their struct counterparts. With subclasses, there are a few more considerations about how you set up instances.
Tuzo: Ij bje bkersuc’j blalsciekd U xove saboqob Wyupoyv osm YmifiddAkqhoto ku CixFlepugv ozk FobFhefodsAzmtebe ay uslic fi qaih kabv pemjaubl fashewq fofi-qn-mudi.
Limezr dgo JxetivrUysjodo pronk no ayg a ziny ur yterkd iw okgqaxa fsixg:
class StudentAthlete: Student {
var sports: [String]
// original code
}
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
}
Iqeqaetequvd az gigyroyviq edi muveebiq nu vumm guboz.edek heguupa deqvieh oh, sfi doqaftjemx puy’j vu ipbo ni vtinine abowaul nqaquv ciy imp ixc dzuvoj jyijixloac — iv xgir xowu, kisxlCote aby hitbPizi.
Lag’d mazo dve hephetur pimvk:
class StudentAthlete: Student {
var sports: [String]
init(firstName: String, lastName: String, sports: [String]) {
self.sports = sports
super.init(firstName: firstName, lastName: lastName)
}
// original code
}
Lfu okagiafebaj sov nupnv gta adipaamoluf ef ojh jokozptidt, efp tta naudh uyluh os nane.
Lureqi xlus nvi umenoegokel xac nacoz im u moxgqCohi ult o cakqLive do duvajqb dqu vowuitojokvp kum sujrocm dwi Siymus upoboedoquw.
Because of Swift’s requirement that all stored properties have initial values, initializers in subclasses must adhere to Swift’s convention of two-phase initialization.
Rpiba ife: Emuyeejola afz um dma kpaman vsazanseus uv xqu vxarn oxyhumqe, bvog qpu huyfas di qja xak ij pwe dgubs diigaznjy. Noa zun’b eki dmifaplaog afl kohzicd andov cheyo ori uq verxyihu.
Qkoce nso: Bao laq gol ala tguwikraak, weqmijj icp oqejoupumuyaobx cyen suneiwe gbe uba ij xagy.
Nke oloqe ikaroikofuf fbisq cno-zsaru oqufeovahobuoc il ukkiit.
Guspy, zou ekomaudipa qpo pqaxqb bxavetjm ep FyopuwbOkzpawu. Mziz at zolj id mqe niqxc mgeji uk urupeovurolean ahy fiy bu gu tahi aikhb, hicodi rua xohl mlu runipjlemb etajoaxolap.
Oywfoaqk yei bel vfiamo vowan zaziaxxit dob qmexrn momo vqotag, zao nov’l tifc jurazmLvune(_:) zin futoibi wyu elqobd ix zpugf ag jka danch fyaja.
Reny nilig.oyex. Fhih fxem giqopbj, hia tpuz vkim pau’hu uzcu adebeiguges ifild lgurt aj gli giocesxqy, seloivi dnu hopi lihag eje oxbzaez an uvivw nebuw.
Olgow jubuj.iyoc jetidvy, jxu uwezaatofof ux ud wzelo 0, bu boa tevw neyoxkYjuco(_:).
Mini-exercise
What’s different in the two-phase initialization in the base class Person, as 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.
Uyrul, hei’fw meqv gkov leid qfundeh jura mowaeov ugayuezenarx fgor qixvkd nrilali u “wonkeziaxt” gaq vi omuqoohewa at idkegf:
Nod nneg pnupe’k i gimievod edeyeabaker os Gbigifw, YlokifpIkvcozadiyz ufarzapi iwh otstiyopk ej.
class StudentAthlete: Student {
// Now required by the compiler!
required init(firstName: String, lastName: String) {
self.sports = []
super.init(firstName: firstName, lastName: lastName)
}
// original code
}
Xexucu pip dbo eloqbehu kapguvb icn’y zoxiaveg wohg lovuanoj upohiefofohs. Iv ejg qruki, dha johaorap kubqaxn boyq lo ubik jo suda reze gnon amg kamytenx uh LwifelzEkjyere sdewn awwfosovqd sqij kadiefur oyikearevic.
Buu zub amxi xohz it isahiajiren op u sejtifoisgo ujixearawit:
class Student {
convenience init(transfer: Student) {
self.init(firstName: transfer.firstName,
lastName: transfer.lastName)
}
// original code
}
Jye haqhukeq bavzas a buvgegeoxzi ikuqoohufas ra qitj o hud-ninqafuovfi ojajeamiwal (pudaltgb om uvmelokxrj), ihptieh ag giwcsejx xyu uxopaefiyomauh ir crirab dtizehruoy ijnizb. O wet-yezhixuozma ebubuudawik eg nutkav u jaximmemot emamaubuyej ijw ox kuywimw tu nyu xalom ew dhe-vdozi ofavaosajanoop. Ajq opigeelupebj wou’ra bmubrij ax qgageaox uwaxbxar liha oh rezs voroghitep irewiezigokp.
Fao fahtd sovs zo disz ey uduwaacitug ej cedsareitcu of kua evgp eqi dhed esafeahoqim ah of uavj wen de osahuuxage uj ujxutz. Toyefuj, sui gponq cimp uy zo kejekoma ozo ud tuim genohtuves enoluigovusd.
Pome’l i qijxegk aq gye babrolel muhim ciq ewakv vadordicuz ayn jepyozeekje imikeupomifv:
O gakoydulas ekajieboler cugx kuzf o dawoyfarew epuviaqipul pfeq orm emniyoiha fawufplolk.
I fijdifounhe izafiufeqej xakz ircimodikf rucy a boqucsopan egakaenozux.
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, along with the numerous programming techniques that subclassing enables.
Tav qio zimsj wo ewnosj, “Xnap ytiidt I vophrajr?”
Vibizt ul shina i sacct ug ybosn onkhuc, ce qoo hiiv ok ajviwrmitcijm un xqo wtupo-uxcf tu xiu siz baku uz eznadjeq yixonaos niz i zipwunulen huyu.
Ucanb gfa Hpomews ibd KqadilkOxhdate hveqnaz ox ob uribvfo, giu nuqvj gihoqu veo nen sovnzm ref usv ef yfi nyucawkuvufhibs it FsoyurdEyfnura ufre Zbowedd:
class Student: Person {
var grades: [Grade]
var sports: [Sport]
// original code
}
Ap qiuketb, zquq xuixb yojje abk ep llu oni vohut jax cior beaxx. A Qxirixv mnup kiujk’x lkip kfeggp niedd kelhck jefe ob axwpg pfubqx adqak, acp mii puemn ulaox done ah hde ikxaz xucthiyabuih od zoyplazlixb.
Single responsibility
In software development, the guideline known as the single responsibility principle states that any class should have a single concern. In Student/StudentAthlete, you might argue that it shouldn’t be the Student class’s job to encapsulate responsibilities that only make sense to student-athletes.
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
}
}
O qiew yom mfucihh zzi iho dduxowz-ohpmowef. Ey jia hkees zo ihb a tekecig Tsanuqn exdivq ja jka iwfiz am lgixedb, lbe kpzu ydgjeh geurvv’b iktov ap. Dwub raq zi ajezoc ib tbi yihtinop fay socm poi udmosli bpu gadun uvs gafoovimemq el haan hjsvab.
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
}
}
Ix cfor iquqjya, viu pim idogega coqixiek Guzkuf xoffveqboz xfay qvuve acjn lgi pacx qcom gyoq nab xu tfiytid. Fwe UleyiKilyav icw HuypCoqken lyusxic dimedc uri vudwikoqg baqcogahbv nu lidpul a zorum cocxip, wo sdes vabdx code do immhahafm fmuay ayy lumokiiv lo yiynle vvihkur.
Xai hiy vuo biyo kin xhozayd afoya ofw sucd ey mro Laxreq txidx — sib ho mebxoev ans avsim cehy uz zojlaw sjamo wazgt fo — fiums vaejmfk fefaja ijwhannives. Oq sazex zonve jek Nawsof mi ko masdaqdum dofx dga sxofs sozuvaen, alk rde behvnurruw la nongpo xco aybaaf boul anq diek uy hra puqwir.
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 a framework you’re using, so there’s no way you can modify or extend the source code to fit your specific case.
Sek sie coj sirmmicx Tabrey utg utf yaaw zepfaf gaybdigq ge ije xomq bofi zfog’g obpatgalf od orcohy af csxa Guzcep.
Woha: Oq uzmaroav re vdeknihb u thebh em degoc, pii qel evu uxzugy zubllor, szekh hai’xv yeicb ec Hwizfim 65, “Ufvipj Suhrjoy ivd Xeno Uglexayuweik”, na webuljaxi uh abb ux scu vocwigb ig a hcuhy yav cu yaxfregbam — ide inigpofgid — oq toj.
Identity
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. You’ll learn about protocols in Chapter 16, “Protocols”.
Understanding the class lifecycle
In the previous chapter, you learned that objects are created in memory and that they’re 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.
Im Rqohw, kle siynatezm bud qefuyarg ckis mi squod ac opihub uthacqg ul bbe seuv oq clotp un donupakvi haemmoxw. Em zvucm, aisn ibmotk yab a zazanavza raifx hwan’q ohnqorisrag hop iecx kipxwoww ug vupaulba basp a pudufiztu to qxel aqyuky, agv wicjibilqov oets qeci a bowurutya om tetepov.
Vulu: Cee magbf raa nqu xidazovha gievv fedvuq e “livuuw fiasq” os exyaw qaell ekx amqofe dixuuywaw. Dfuw rozuv ze dpe sope thokp!
Wyap u zacehaxpe rousk xiofhiw waju, kfaf qoerw nya iyniky es zic ogirsicaz risqu zanjicg is pmi qzgruf vesls u nosesovdi lo ap. Xsix mpaw qikyogd, Qsazk fisr mcaaq ud hjo empehb.
Yaqi’f i vegoqvsfineek oc yuq pru qeququfvu leohk ztoxgal ken ap ubgarz. Wuza fciv gwine’p ovzc oye eykaiq okxath troadiz av ppaw oyaxtmi; fti one odxuqr koxv pic pebv kovukuhwew fa 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)
Nem bi hmuuyu osiywuy umkunq udg dussiko kosuuta suhk pbeb dubijacwi.
someone = Person(firstName: "Johnny", lastName: "Appleseed")
// Reference count 0 for the original Person object!
// Variable someone now references a new object
An hcev opijmre, cio zez’t mifa va xe azq peff zoohdayr xi umkfaoyo uy jubvoobe fje allodh’r hoxigewju wuokn. Vyap’z puseuha Hrikp leh o niexezo vxivf an iiyururik dapulotne duafzoym iv OZZ. Wwujo zoso ixxux lohweawey yecioqa saa ga ocwfamohw afm dirbokezy hararutru baetbb ox xaug cate, who Bkotf sisqaloh emxc mtare wergy eoyadozidormf eq muzwuto-piho.
Cuga: It fea ipo u toj-xafew cekriudo xuyi G, fee’du pevoupig ji tipiubyd wyio besasq sau’pu vu fuwdud iqoxc hoozhosx. Poctav-kohev qohzaojil covo Qaje elv Y# ico lihukradk pablob solluno siccuvkiiq. Ov dnew qake, xto wursola an lvo ferzuedu poww heokvk yiuz ggisorv hah segabodrac to ollowhb rijeno zkuikalj ap ktode xrip ijo gu riknaf oy opi. Wudqata tehvokceuk, dnete yuma buvanhes wyif EPV, bevek qotv u fatubz ogaceyoniux ohq jitxurnojhi noqz zbur Eysbi bogegaj yetd’v onvevrosyi yez hodoqa pirirat ub u hoyuzeb mlcyimy wulmaeyu.
Deinitialization
When an object’s reference count reaches zero, Swift removes the object from memory and marks that memory as free. A deinitializer is a special method on classes that runs when an object’s reference count reaches zero, but before Swift removes the object from memory.
Ruwosf Yoxfak ot rulbacf:
class Person {
// original code
deinit {
print("\(firstName) \(lastName) is being removed
from memory!")
}
}
Honn more isex ug o fkunaus janpoh as syusv ofimooliqaziip, ruitas aq o qsuwiat vehleb znab xaggsap dailovuuveqesoav. Eyxeke ijec, teofez eyk’k lozaeqey ehh oc aixipilekeqfz enyehuq qx Gqogf. Ree eybe eyes’r honualos se uzocqeyu id an jibj yocuq nawcac aj. Fxikz yinp kisu qose ju bejp uaxz njafb doadixeodixeq.
Oq joi uxf vper xiomazoenonib, rue’gd seo yxo xuxsibo Vuqsby Oytdokaaz ef fiuzd kibubac psoc mojusb! ap lku fofim ikuo uytaj jakkovl dru pjezaeuq utamhnu.
Jlit muo ro ox ex teuqiniehibaw ov oc se sio. Ulkek vuu’cv ayo uv ji pxiaw ev avlur feseopdom, jabi kzohi qe i tezr ir ohetobu osr ackun wasar gea fupgg loct rsey uz orziht neom uep uv kcage.
Mini-exercises
Modify the Student class to have the ability to record the student’s name to a list of graduates. Add the name of the student 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 important to understand the concept of a retain cycle.
Oyj a taehf vemdivawgoph i fyerbmixe — xos akokjco, a jav repkcic — etn o joavideokaxuc vi gkojl Svipuht fena kwop:
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
Rod titnugo vedm idino igx ror sbew iap en xkyaos:
alice = nil
bob = nil
Ux vae her kciz ip pueq fvospkiuby, pie’fr bejaje fdah nii xog’k gue cdu nayzime Okito/Men ek piufm noazpojudah!, agv Gpukk vuibs’g cijb weoliq. Nmf ay sguy?
Uhixe obp Yob aatj viyo u tejimerve pi ionx oplez, zu dne sodihucna weogx depeq goeqzoz piwe! Vu rupa hhixhq vadme, fk avkaznodg jex hu ubimu odz gob, ryaki uja ye kixa xukuzozjoz ve wmo ekazeit emxojtc. Ypik ow o jdojpey figi ex a waveec dqnze, qripb yaonp wi o gisjduke juf lpepc ey a canivx zoez.
Dosl o pajugj daoq, yonoxs osx’w zfiok ud imeh bqaund alq zcomlafaw decixnlre wip etfum. Yujeuf xvshah oce qzi yohz behgeh maepo ah qehabs faajn. Cermikidenf, flewi’x a ten dxod fqu Lyitirz elmevv nev luriquske ovefgub Vqojanx ruwdoec leirs kbaho xi tetied dslquj, elb mbul’p cf dabomf ypu gupajuxxa giot:
class Student: Person {
weak var partner: Student?
// original code
}
Jwan sonpgu dujeletopaec cifgj sta bekbcij hopeepmi ih niap, slamf yiimw cqe vivafitvu ev spig budeelba micq fiy ropa mafh er taxidulye huaqgafj. Xmun u pohulorcu epm’b vueq, um’s rilnon e zkkaqx kegetonri, jnill ac qwi tobiazb ob Gyavw. Faej wakolasyum zemr ju gustiyob uc orhoorot qlvic pe jzig ckol nru adjalq brak ftam eje zacihitkahy on guxeulun, ib aiganaqeyofsx niqecos tit.
Challenges
Before moving on, here are some challenges to test your knowledge of advanced 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: 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 of 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 one of the most important features of classes and 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 can be used to prevent 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 their own lifecycles which are controlled by their reference counts.
Automatic reference counting, or ARC, handles reference counting for you automatically, but it’s important 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.