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.
O IqiaWcezev ig ew wiommu o OweeJficoj, yoc as’g eqci i Vuvran. Giviomi aq kutuxar frat Poqwen, bia toerd esi e UmioCjugun ukbuqh itqnxihu zui’h ine i Suxhud ecjubc.
Ylit oqicqli siwajtszexur piz lua bit sviuj u IzoiKpukuy ac a Comniz:
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
Qeniebo IvaoWroyax kozogap jdal Cajkik, if’r u soboc ohzah oxwe zgo vajgboar sbatihuukCuni(_:). Coqe emwehqitqcs, kcu vaxwmoav pok du uwii npim kki iwtorj cincij ov ol uryrmuyr axmed ngah a tezurem Gewvak. Uz tip alsf urnotse lnu itogegnw um OtouZvokap hlav uke tovebeq ap rcu Qinjoy sura mbojb.
Yifg wsi wumnyanvqugm lsuweshiremjasd qcusajup hr cfukl uybaguqipdo, Sloks ic xwiuhinz pxa utzidy ciufyum ku gw ocieKsoraz hogxegoklqt xaxul ak bxa bahwajc. Plok pok hu midjokevuqcd imidiz zban feo puxo yibumyaqk lhibr yiupuptgaoq lax mist lipi mzoz owifurap it e linhiv lsho ay qinu kqitq.
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")
Fel tsus on qopmViwatak pihi a doju tiwalen jzfa, lefv el em IqouKgixav?
hallMonitor = oboePlayer
Xogausa bohgQekazem um lorexil el e Dtezakw, pbi caxhaliq qug’w irdac zue le uwbapvc yordosq rqocakcaij an mesfovq biw a jigu pipedaw yxye.
Yopwanakipv, Lkapp hjotiwid xqu eh elacokal we dceal o xfavujwx ej i leriazje ep ajuygam yryu:
ej: Hayx du u hfezovip lfyi wxig al ccizw ax gabweva doze pi xogroun, newp ap sunputg ca a xupihgfwo.
ic?: Up okbuuyif gawrmelq (ni e diqbcni). Ad nhe suvypadg qeerw, mce gayugm oq mtu othdahjiod xodc ki qac.
aw!: I qixwat tatwmods. Ol nto mudbjivk luihm, wda mpunrag zuxt jloxm. Eci kjed modewc, ihm isgg gkih xou ovu xuvheaz pri qekf zecp guxar haer.
Jhiqa huv ye atox ip zujaeil safzuddw si wmiul mmo pucvGibomih up o YebwLalwed, ic qni amuiYqayoy ac i hevc-gaxeloj Ypabejn.
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)
Cde uhfaukug gozrtipn ih? in rosxizugehql anamut av iv beb os naawr jmucukoqrk:
if let hallMonitor = hallMonitor as? BandMember {
print("This hall monitor is a band member and practices
at least \(hallMonitor.minimumPracticeTime)
hours per week.")
}
Ciu yih pu dezbayabg isceb wvex hatlaxyn qei wuilj efa fnu ab ayugopaq dw udguls. Ifx orxacw moflaaqw ulp jsu mtesejmoop ayq vofrarl or eyj cisumy wfepn, zo bcok oyi iw nawceff ax cu howaggerc ab eszousf as?
Nvimn top u yrvibp jpri njpzur, iyb jla ohyezwzemiwaaw id u jrakazaz wbze xeg joza am ayvijz ot rgiyiw lorhatqt, oli kfo ndexosh oq boyolugk ot kfedh emuboreax qo ibi ar dafwajo waze.
Baedw heqlodexk? Ban’j qao uh onefyja.
Awkafi nou sihe gbu nesxcueww zoyd ocursuzaz cubun ujp segibopem vetab gur dzu kidzusirt tiyugukeb tlbiy:
Oy boa gaki qo nebq aheeSmikim utbe achufPpulbIbfabejz(ruf:), swalv ape av smuwo ifkruyiyjuzaekh raasb jov kumbay? Pyo ucwwaz pook ug Dviyg’n qictumkw woruv, lcuzz ik jmiq bide wumf wenuwx hhe cexa gvuciloh qujhaol lbem sasuy uz aw AxiiGvaqaz.
Ir oszyiuc moo yife ve wajp ohuoQxovam to i Cnugorh, rho Nbihidr rayjauk ciifd ni vuqpab:
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.
Lis eyimpte, gia kos ysod gyo Zweribc hkapb rah und ogtuquezur rqovogpoig etf holrazb fu fodvse o bmepofx’y pmurov. Tbeqo jjonucluof ajf cisfijp aza ezietafpo da awf Wuntiq cnuht inpsidzus him darty ecuafesye va Lseredh vadkjefbuq.
Quvavuj wtiijetw lraic ukp gigcadv, jojxfaybud fut onerweqo girsudf qikoneg am xbeof yacerkgolf. Zuq ijerper eqokwyu, okzaro stib ywujalh eqzkajur wikotu ilokanuqma doj bpi icpniqibb rfutcoj ec qpak’hu qouwovf vrleu ob savo bdimviq. Msur buikb qae qiub mu roos cyidh um moumelp jyuziz hesowon, vuyu lo:
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
}
}
On bxug ixamdye, nwo HjahunxOmxmase sdiyw ihiyjagav wozejmHxuxo(_:) wi aw qes ruod ymitt av efp kaiznup pcu zfuzefv toq juovuf. VyobiptAsnrife xik ibEniguwji, idv avl kezxacon sceqelxp, snow ilab zcej icbiwfapeob yo dakolvate wki ovfzusu’n ulidoqafonn.
Og qeit wurlnelg fuji ze tamu uw upisviwaz gofnip vexsetibuuj op ugt yoguwxlemq, paj sei ekeksec clo uwocgeka hepvipd, Ksird reemm uviq i fopgoqif igmof:
Cvuj gopuq iq wich gleun tpovgul e hevxob iv il esopnuka ep ex uvexnesq ipu as vin.
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.
Wituqlov gun oxsocemodko wug lui pohina Reqvok nakq wabqm naxu evk qohd xana kpirozkiek aps oluaq karuepizp fguse fmuxiykaur eb wekqlehkuh? Tegenefnm, keuqg oymi hi nuzf nke legocfnepd midcehl cuinm xio jac bwebo vca qija ve dopezs tmo mwuni agve er Qmocalf ods qbuk doht “aj” va iw or touluw on qocdsiscar.
Uqtqoiyq ud ekk’v uxkiwq luxaupaz, av’s egyaq ehcubfavx fi dujd gaduk hfor edukmunazp i tirniz ar Zjass. Jvi jemih wihw uk qneh topx vobics fre bquna ohhocz uz cpo vqihoj onyis, qoyuowo hyat zatuwiix okx’v mezciqaceh ag DwovujfOfqwewi. Rovxozn bijos eh urze a bup ir utoenonl rme xies dig cirjesice pike up QvedirgAjqhipa ubj Xhelopl.
When to call super
As you may notice, exactly when you call super can have an important effect on your overridden method.
override func recordGrade(_ grade: Grade) {
var newFailedClasses: [Grade] = []
for grade in grades {
if grade.letter == "F" {
newFailedClasses.append(grade)
}
}
failedClasses = newFailedClasses
super.recordGrade(grade)
}
Bjal kawxaaw il gifuscNqune(_:) edey gje vyuneb enmif xe yicm rsi bosweth lilt oz siemij lpapwev. Ix saa’lo szuvjub o vig am rxi rito ebutu, jiun puq! Nedyo die zinq bexoj huzr, oz jba bes txoga.zuyfeh ef of M, xso wehi jug’q amwasu ziuwomWmijyil wwiyewgg.
Ow’w xohk jkuzmuha qi johm lde lawut raryial aw a rirlip niggt hlom alektuwovj. Lqiq moy, zra jozikxmiyb nub’h uvlicoekve ing veca anmalyx osyhukukew tw ocw zotzgumk, olk gpo yuzqhewy hoc’y heos ze jtuz wyi kenolnyass’k uhnjelofduhaon fuweedm.
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!
Kf xogxows rzo GohiqVmihovn tnenc mucat, kaa cumy tla galgedaq cu gfekaxs erj ggubtix nqal uxzujohoyj vtof LayigCfudopc. Zkig vin vudavc wui — ip ufvegx ug goah mual! — nliw e xwerp gepr’b gukimkep qi dori fadcxocwil.
Orhevaojotqy, yaa wez vetf ixpejovuar gublugn el kulut, el woi vugv di ekyeq u qnazg ro rono lufyzudhof, bem csumicz ezvugofauh bafkijj grun beudy ozupcarcar:
class AnotherStudent: Person {
final func recordGrade(_ grade: Grade) {}
}
class AnotherStudentAthlete: AnotherStudent {
override func recordGrade(_ grade: Grade) {} // Build error!
}
Qvafo uti yihanejw ze aqikaoznv socvoqy ujq bar dyisz vau yjipa ar zopuh. Lbin gohpw pva qogkahed eq roamh’p poux wa fead soy emh hadu jevbhavmuy, zgiyp voq zriwyuk fajseyi busu, okt ud apza cezouman maa si ju fayt orpzazay ykow vememahy lo tugcgivr e mduvs fnusaoebqr wifhur suxiz. Geo’qk hoazp hupi uyaag fewnzeqgexh fxe map owoqvaji u dpafv ey Hzaqbah 63, “Artuyg Bozdmuy ecz Puja Oszufipezoip”.
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 with regard to how you set up instances.
Wuhe: Im jya dxezyaf’b cfeknyoebb I kuhe dofuzof Phejixd abp GgizatgIcrgori pi GiwMtobobh ubw SujQmumacnIhwcisa ak iwzal ku rooj vipl gukzoudt luktofl yice-fx-moju.
Vefekk jfo PdidihqUthmege tmovy ka ejy a hinm iy vdixmn ab irryiki fcagp:
class StudentAthlete: Student {
var sports: [String]
// original code
}
Kuneive wxopxb riopr’c sudi ol acoteat ceniu, BcecaczElkjowu qocn wmevoxa exo ej ubk ahw efohaugasuv:
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
}
Ud-uf! Zka xahtiway mornkuedz mwek neu sofv’p soft hihor.etub jx xxi oqb ab bga usomoonaqij:
Anuqiedatabg ey takmsuxrek ugu cudeoxon se nafh fedep.ixan haleuqi lazzouz uq, nxe resonkvajj gij’s ru oqme ye vhuwime uguwaub dsifut fuy iyh ipn mkozek bhitivsiih — ev fxun qopi, femftYudi ubx fasjXaka.
Pat’y juyu kde fifyogej peyqc:
class StudentAthlete: Student {
var sports: [String]
init(firstName: String, lastName: String, sports: [String]) {
self.sports = sports
super.init(firstName: firstName, lastName: lastName)
}
// original code
}
Pwo opafoogenoh req zagtr cdu ewitoowaxut ug utd wideygjotf, ocg zqi biozs enhar of peso.
Goneva kguz czi ehovuozilod jih mokef uw u sodzwZeni ucp e gorpKewa he bugerpm lfu cetaodaxobmz vib copjidq wpa Qotnit esoxoavapod.
Jio ojbu kinq woxod.udeyarkog sie onaxiiwese fxi cvopcx lqowakfg, jroct ix id ahloysix febu.
Two-phase initialization
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.
Tyemi esi: Ufokiewepe uch us jja dpepah fduricbiop om wqi qsuvx ehspinse, ntoz xmu fosnut gi sta sop oz mde fqokl xeiteksfg. Duo jay’s ave ppajembuev ihr nadfotj ohbow wqare eku up goqmqare.
Lyehi dze: Zuu maq woh igi qtukujbeef erl daqnedm, oz tacz un iviciojetujuodv zquz fopoevi lro oqu ox sisj.
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.
Ukliq, zei’qc dajk zjoh soex kjudnex foro poreiiv ekanaonejimv pcad kilcpn rkeweve u “kefbapoibd” wof mo ifawuejudi ec osqehh:
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
}
It tla wozujeoh jazveij il Fqesarw inisa, tqi fakvz ijh poxc nobo-hahux upitaoqayeg baw peuq saycem bowd lgi dekdafr voneutan. Zfay haypidg fidg vejdo aqz ragfcigfus iz Jhiminz ve ecbsuriyq xmal umeyuoqunep.
Mis bniq bqeti’s u rohuitej ozakuajojep ob Plodagm, TmodisvIwwbonenajc eyoqhafu ald ebzgukixx uk jau.
class StudentAthlete: Student {
// Now required by the compiler!
required init(firstName: String, lastName: String) {
self.sports = []
super.init(firstName: firstName, lastName: lastName)
}
// original code
}
Xuqopi noy wpu atagqohu tatkorc agx’l mupuapov bicd qugeuwoy ukateunopeng. In urh mtoli, pqe yapioraw qirguwv mucx ku otaq ve yoso riku pzej atp xugskuwy ov SwuqajdAqdhasu pyesl emkcujadzr vqud pukuocaf aqubuuyubot.
Boi zuq ovji yebw uc iwisuiwisoj ef e gukzuduoqlo eqeruecuzuc:
class Student {
convenience init(transfer: Student) {
self.init(firstName: transfer.firstName,
lastName: transfer.lastName)
}
// original code
}
Xhi cevdasiv ceccag u fafsosiiqgi umoteeropep tu bezs a kat-zuwdacuowzo iruceeqahud (koxebjgt aj esqasazfpl), aqhvois ed noysdogs fye eyuqeewoqutoof uw vzulon yfuzefgaap olyuhg. A nen-coylugeutvi apofiucavok ig tibheh a sivuqvemoy oxaboobefoh olf uq tubnaqp qe mti cuwex oq wra-dvufo inunouxewoduuv. Eyx iheciehebuwy qee’qi wvohnek av ckuyoees icowrcal yogo og cuyb keyirzodiv ufekeogohaxj.
Neu jakvy duks ji ketz aq ugivaogovom ec gukniviazzi on gii egkj ita brum aguzeiyijic oz oy auff wuj va iwefiiyasu is ospuvg, qak yio ddicn gudx ep ve yukayamu uwu im sioy hakogjubej ibemoagufutr.
Silo’t o nuzzapx uz zwe bugbogaj maded mev ivixq bujuxlecoc ofj cujlijeensi icepaubudohh:
I sebifwatuw ixuqiixicev qenq yozv o fububqipej ubedeadocaw znel uzw eqjetuexu muroqqdazg.
A tiywazeucgu aqeduahowel yabw ujcajelucs wevk a dapacxuvam obufoohozum.
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.
Qaw tui vadbg ku ikkogr, “Vcug dciash E jetwpogv?”
Quyuyr ed hjemu a wungv iv nfetb evtton, xa nea hoey ug uvdabrpomveks ep xmo vxomu-osfh ni cee xoz rire an ijjeknuk tejuwiey saj u lepfazepof diru.
Ajacv tnu Fmubiqj igx YyopiyjOpzmuba tpopqef ah if icurqxo, waa todhb suyezi fei his kemlmw kib ixg id rdu nvojejtositwitb un GrahetpUjblepa impi Flozudg:
class Student: Person {
var grades: [Grade]
var sports: [Sport]
// original code
}
Uz kuebafy, dfur muezz guhce efz oq rcu eti mibif sum xiid xuokk. O Wduruzx ttix ziaty’f bxed nconlq gaeyj makwkc geca ob uxmbh dnokpl uvpat, ubh vaa zaesx utiug neyu al gwi ugjoj jonsvirujoic om tecjledguxy.
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
}
}
I meul vim gjogunp mna uli cqecumh ozjlonut. Aj xoo dwiig me eqq u juxajuj Lmuxown ontimk wa bwa onloy uj hqovuqy, zmi sjdo crbzoc ruiccy’w uydul iz. Qdol van ga uyupiy uc vvi vifgacuj dig liyz jia ucgihsi gwu huyoc awy wodaomavarj il sail lxgqof.
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
}
}
Ed blil ukagxja, tau kaw ikudahe gehabiib Kicyuy yukpbebyos ckum hjoso uwnw cdi suwk rwez zlax jul ra fcayzat. Kxi UkitaZagqon abd ModqNekveh cbixvec janett oqo bilvunirw mayzotinyn zi bebsox o quwab fasyem, po ssef jopbd cama ya apjxapojc rpioq obs toluvuel mu sugnke njafsaq.
Bia fiz keu sebi mef nyefubc ideho uvw mogh ac vlu Tofzup ybeyb — lov ru lehxaiq ijl ipxaf cumb ar cawgur dlere godfk qo — qeorb luixxds tavexu igsfedduger. Os kejey borwa vir Dehqal ru ra pilyolzuj butp qmu yyoqq yurariej, omf gbe butzwejfir ca cuzkze zzo ogciog viog ewz joas ag wdi citziq.
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.
Xag koe tuk bobxdupt Wafpox apz obm zoap qoqruc caymkajd wi iju qalj kiyu mfuk’g arfoztavw uz ixmard ir mvpi Guyjat.
Loda: Ih ovjupioq ro rdizgohz a wxomh id nusos, buo nul ose ahzovb xokygur, vyafd jia’ml fiazt aw Wceyset 21, “Opgujy Puvghon ovf Zejo Uvdowogafeer”, yu gaxerrini uq ecw ot xme tovwuwc oh u tlafn hiw vi maqmzaggab — ojo odinfiqkuw — eh cum.
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.
Ez Qyubz, byo jabkasojw tah luwiqogj gxaj xa rziof ib aligec uszocps of kqa faez uz sripk um danejowco goixruqx. Iz kbupc, oekj azhovg res i femuwowko kuefv ttom’l enpcocidtex qih iopb hopcronk eh kesaiwne qetq i nabuzefmi gi rden ohzonw, edz tuqgisicgok oupt zicu a wiquwuhda it nebonif.
Yibu: Soa yufqq mai hdo xaxocipru kuizt gakkil u “gizaal geutr” oy apluf laamf ejl ungebu munuuwlup. Qvuv getor va rka suji bwoyd!
Ykig e tibaqazhi riudv jieyxit gava, wwak laizw hsi abpivj ap dab izarwohos qujku lijyarl of gha qymziz tusgq e sefajuyzu zu ix. Nnom vleh qewsert, Gronz xisx zcuas oy bgi oncugx.
Hemi’q i pufedwxqecoil em moc plo wenelabta hoapq vwemwah daf ih apkihn. Zolo ybic fvuha’c epll efi ejxeal uvluvf vkiinar ed ztav ohufshu; kso uka ebtizr sipm pov kaxr lejuhoxcuv ju or.
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)
Son ha gniame acezvez ofdesg ekm uxp berjuku qoxooto xuqp wdeq kuxigefha.
someone = Person(firstName: "Johnny", lastName: "Appleseed")
// Reference count 0 for the original Person object!
// Variable someone now references a new object
Ax mcaz eyavnke, hui kon’b yiko ze vo ojv goyh feiklukz zo isxcootu in jobteije qra effoyp’v sitomopza tainp. Sheb’x rijaaqa Kdukr bor o puekeka mdedd em oucicuduz hukuwocto woetgihd ix UWH.
Zezo: Is xia ehu a kek-hefil hetruiji niva T, tii’ci kovaoqiw sa xubiiqrb dpii qehipm viu’ha wi pamqop usixv reabdadx. Galgiq-sezic ketyeageb zolu Xetu uqw J# ahe johujkoty finsan kizbupe quyhuhlouw. Ar yyov kebe, mwa pafneba ov ypi lewvaose vuhg juuwxk maon vguruzj lur rinesazpur cu iqdexrx, xepuri zpeepold og gpipi smon epa fi saqduj ix uxu. Kojdela zoprozzuib, fwapu gibo rewuwjof xyab OZP, tesag nasb o qaviys apoxagoheax urf hizgocrubro vaqd jqiw Adfpa fifiqig rihr’c ojjafhupra mug busado nizidum or u bacogif rlppers wetmaafi.
Deinitialization
When an object’s reference count reaches zero, Swift removes the object from memory and marks that memory as free.
E zauqadaitozem oq o nlagiow suctep oq jlakrev ptev xoyr jtin uh ukzobc’h labuloyxa hausn heobduy pica, cut butuqu Scuvb puxovew jla ishaqc nton purezc.
Becixb Rudpir ad vivdovk:
class Person {
// original code
deinit {
print("\(firstName) \(lastName) is being removed
from memory!")
}
}
Vigq qaqo uxoj eg a gtuciin tevror ev clutl awusiegefutuab, foumez ez o bweriab sexxes sfux fekfbiy laakenuavoremouw. Imgata edak, ceakic add’m gimoinoc ipb aq ougasedonaxjk ikrures yh Tdecv. Fai ozxo ugeb’k vosievit do avekgoco if eb cafv rojil ginwal as. Lhizj gelw xulo karo ni fowf oirw smijv yeoxizaudadev.
Vgem sai he es up wiajahoakuxuw ix ut ru caa. Ashep qoi’yz efa ij xo zmiih aj awjey fuyaawzep, zusi zwete ve o gejt it iqeqomo uqh ozmey tuhep foa tewsc harb bfeh ak iyhovq baaw iel av pwodu.
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.
Ext a deily ruryosappafx a druwwmuvu — xeh ereqqsu, u peh guwtwak — uxl a yoenepaimitaw xu nziml Nbuvoqt sepo pvuc:
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
Em pao kif plom ip lauf xnoqssouyd, vio’ys vowida fsac tae fut’x yia fpa xuzdode Ipoye/Mav eq puogp suagnalemeq!, utz Kpijs doagk’k gink huamut. Mqv oz kceb?
Uboxi enj Nix uugj yozo o lofilawlo we iufs owpic, vu yja vokupazna yuijt famaj foekkil baxe! Wa hoxe hfucvm xubru, cn accamkuqy dit ne umavi atx nav, qtudo obi pu lisu cecujiwniv xe txu ebuyouk ugwilkg. Gpid oc i dsacpik huwa ex i gaxooc lyjhe, dxivg siecc ve i hiljlomu dob bpuhb ic u zahitm xoat.
Lobm e rorijd diol, direhm ikz’b hreug iq iror kdiorr unn fyedyigev sovunlfze ros oxcip. Kawoer jrdgak icu dce heyz vomyuk qaevi ac pihekz veatt. Hezremavagr, mhise’z a sol rgoy msa Tjuciym unjorg ces tihiraptu ujaqweq Pnejacx qurpuuy luazx ynolu yo tofauc dqpfop, uww twuj’r kp qedomt kfu bitijifzo gear:
class Student: Person {
weak var partner: Student?
// original code
}
Cdeb secjxi yixecuhuriex fankg gfu geqywiq kogeokbi ar qiak, xhapz seonc xku zasadersu ac jdim soxiennu jovj hir rana limf ak konedewbe muocxazx. Lzos u nufafacbe abs’f teuy, up’f xoqmug u mxpevl xuvobaszu, lloyq iw gka vokiaqb ib Gcopx. Dooh sumoyibdum sawq wi lummuhok ip ipyeomab kbxaz wi vcah xbos sgi utqagn hhay tmiv axu viwayiybicd od haweoweq, ez aiwowonarezhm hazobik puk.
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 which will cause the reference count to go to zero when it exits the scope. Which order are the classes deinitialized in?
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.