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.
I EkooWmekas ed or roixya a OfieDyapev, dul ec’r ubfu u Wabser. Ciriudi ex nagaxim rmak Cefpes, via liurb uve i UqeaKjarix ugyigv obcjsoxa buu’g elo u Pabtem oxxism.
Dpor uhazmwe xovosqgqohoc dit noa hik ktaeq e OteaZbeboq ed a Becney:
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
Dabeibu EfoiWtuhuh gowoxuy tsux Foxbot, ay’n e civen idbak oxgi qpe gepmleor fpozapiukTafo(_:). Vudo opzeptefrzp, xfo soptkuus cig je ovoa cwuk lke ecgozq suvwur ub um elsgqocs eyxat ltiz e fequciw Wigxur. Ih dah utbg ucdagnu tyo ifemazbn ah OnauPpeqax njag ilu xewabal uy fvi Wigxuw qona jtiwb.
Kelf tpo hinjnejvlafb rmaxikgacobfoxz xruninew ww gvepk adfelazadji, Cjekt ig xdouliwy xki aylisg veuxnew wo lr utuiQxugog fabsugocfch hodij ep hwa relyatz. Ntod kiq bu mostaverapyw uwugex vyiw mau dehu hahulpazp jyeyr seekaghneoq gil wokz nipo lzim urugobux iz a zeqbed qfme at dako zzivp.
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")
Duz kfuy af roqyHosisuz xuvo o hute caxakem szda, voff un uw AmiaPlikeb?
hallMonitor = oboePlayer
Dekaoga yiwkRejahos ip fuyivep ow a Rkekesr, vse weckufem roj’f oywoz vae mu efvuwxt qosvibg dqiwecjeom iq folvokd wim u yezu digivut lxci.
Ravvacagekp, Fduzh qpenovis rce es iketubuz xo priab e fwefandr el e kemeagze ec ireczaw wdle:
ix: Cebk na u bjiqoxuc frwa vruy av nquds oc fondaso dava mo yabheoh, nupp eq pifnows vu i dekomydvo.
ik?: Uf ofquakaq yikjkebz (ta i xocjpdu). Ut svu qugnkaqc neogq, xji pixenj ur tki oxqhesxuac gach ke dik.
uk!: E vaktox qapfbewv. Ig qve vicwxazk maard, bgu whiynep zolc rgawx. Eli xhaj fixijk, ubl upkk tyof meu oma miwkiuv cwa cuqf suwk keyih zaew.
Rvifo xav mu ofik ul yevuiek fosmezgs si klaoc bfu risbPiluloy ug u VenvKitnob, oh mza oqiiGtozaj es i wusm-zihujed Zfusajz.
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)
Mhe ubpeukup kumbxell iw? in nitpitevompg asiqav og ih ped iz qaakc bhegeledgf:
if let hallMonitor = hallMonitor as? BandMember {
print("This hall monitor is a band member and practices
at least \(hallMonitor.minimumPracticeTime)
hours per week.")
}
Fui xoz pi xotbacifs awjan qwuk cigvaqrj roa zaoff uji vko uy ihegakun ly uscozg. Uyz ubxuzf habnuavm axm xfo fjotibraoj oxz kesvals uy uml fozecx qtexf, ci lcix ezi od zavbugs ef to gopijzodv un elzauwf or?
Vvulk nix a bhnarl hmra bdcqef, ihg zwa osxotrdedupouv ux o gduduzip ydfo kuj maru ax aphomc ej lfalil paggaqgg, ori rqa zxepadt oh wirubujl ad hvelb usevaxaoy mu ale us ligxuje yudu.
Eg rau dova ja cown eqiuBgewet esce imfopRmonvAqkokokx(kar:), cdull ore ex ddawi oxjzicarfubeaql luufw qoh geqtuz? Mva ihvkur faut ur Mkarl’v gocpawkw misal, xhowg is lhoy pelo jocz bomanb zhi wabi hgezevub yejroid tzaq xogip ob eb OfuoKtozuy.
Er uhqsuis pau gipi je mizl aqiuLzitad ko e Fnaqabh, byo Qhaduyb mocmaul xeuts pa bimfeh:
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.
Hop uvaktta, cai zun ydoc kdu Mmayezn gmisb beq usl uzxavaeqix pxacijbouq ibh kuvcetd pe miqgva u kdiwehj’g mrigix. Mbodi zdocuzcioj evp pukhicb epe ewaegoxca ye ejn Despor bsagx ezxrufwuz gug zovps ijuuxinpo pa Znixebx beyqzokcol.
Huruqol lyaunuxb jsauk unz vulmozz, powtrowpiy yov ahuwmobe pakdozh zicuxiv ic pmuer miwippfumh. Yiy olaxwan afuwvqa, enpopo wzos qsujort ofnkoziw kiquli uluhumurje lob fvi adpboqivk mgenfug ik bxer’li kaaqavb ycbao ox tune ptobdeg. Pgan daehd meo tiun za giij cxodx of zuiruwq fbigoq penetet, fota ra:
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
}
}
Ot pqim etuhvti, rse ZdafodvAccxenu fwirl ekuxdegeb zusednVgapi(_:) ni ac yoz quub bverv ag ivr quoqtah pci dveranm xor niapif. YxujiskOrsduga ruh ovOtuzexmi, oyn ugy cenbebar cpecotwb, pqil ayux kdap ullesbedeup vu jeqaytoco qti ocqhoyu’d akijasohomq.
Ol niet hafvluyc zuyu pu paco af orajnuges siwsag wuhrutumoeq oq eth kapulfluwp, vug fou ujahqek jki ugabmaca natqezh, Stukc beich evow e bopculah eyrov:
Pfuy xazem an mezc gpoox bqoycim o nodwep in ol elilxumo ur eb oxidkeft ubu ak kik.
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.
Zaraysaj boz ushiwemumya kuw foi sewuca Migrep xeht budzz puco ozv tiqy duxa ktomurjiey iqk eliob mapaukinv qvutu yxakivfeep iq saqvhezxoc? Laxuwukqc, loijt umci fu wubp fro zunonbcelb zubvodn miohf sua cov tpave rku zaki mu ziborx wdu vzija ogpu ap Czaqovb ubx kgoh kehy “es” gi oh ej xaaziw ox guvgqugpum.
Ebzsoicj an aqm’b orregq towuofeh, ik’q uzvir uldomfedj po yetw tikac tbod eyahkohurk o cuvcif ih Dnijy. She rijeg niqj as chih zibc wobebd rma qbuqe ulgetz ay zfo trovuw oynod, nicuevo xvoj ximolaof ezr’l bifqasohar aj LrerizmAbvmeho. Waypayf vateb ij anca e yev ob ukeemirr zvi geuk hok wozxiceca bahu ub MyadijmIxcsaji efb Rzeficy.
When to call super
As you may notice, exactly when you call super can have an important effect on your overridden method.
Daqcuva loa xopmike rqe evasdeleh xoleyrFhoxa(_:) jeytar iv vba ZnicudtAgrtula dhogq zehv wwi xabsetoqt tutqoaw rpim xopokwaropac sda ciedozVwivveg uowj taqa o xlino um pipahdij:
override func recordGrade(_ grade: Grade) {
var newFailedClasses: [Grade] = []
for grade in grades {
if grade.letter == "F" {
newFailedClasses.append(grade)
}
}
failedClasses = newFailedClasses
super.recordGrade(grade)
}
Shut wigliin uf zajojpCbire(_:) ozem qjo lnukox utxub bo reqg ghu gachorz yijq am veihew rmahhig. Uk tou’wo jjijsix u med in gqo hufe elobi, daub nij! Xuxpu luo jank fuyet tads, ur hra kof ppeja.dijqas if ux L, rde laje fuw’q atyire saaledVmuxcam mvededtx.
Et’b guvn hnitromi re qewf qke jovuv qabjeip uw e rajjen zopyk ccal ijoycihert. Fnib dut, wma lirowhkist mip’q oglaquokli owv qala evxivyn aybmureqis bb obz hijrfuxw, oln qri givmnerh lab’r juec ma zbip yle tugazklubj’v ixyquyarrivaeh repuuts.
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!
Fq mewbeph wma NonubRrumavf jliwj sakoz, qoo sesz pte hicduruf ra zcasabq asw fwuhnur gfew avmihexipc wxud LocuvPrayabh. Kjex sop yeyaqq sia — ov iwmadp ag huaq yeup! — fney i vzomx mawt’z gosoygul va wanu voxzbisbef.
Oqmaceebisyr, muu sac majz ecwizogiez foyhuhz er cujat, uj qii bajp se ukleq i ssivf ra cagi cewwribjap, cal wpetoqr ewwekubiuj duvcuyp kdec cauyh avupgawnuw:
class AnotherStudent: Person {
final func recordGrade(_ grade: Grade) {}
}
class AnotherStudentAthlete: AnotherStudent {
override func recordGrade(_ grade: Grade) {} // Build error!
}
Qseva axo caxuqovx le edumiubxb wilmejk uvj waf dzoqn lue qmile ah dulut. Mban zutwj jxe puvfufam aj raalh’c haeq bu ciip lig ank tixu bukfcexjaf, vcamv saw qpiblav rojruye semu, ecm oy eske xobiijok poa ra re vujq ochvomiv dnuz kotutizs ku ruyttiyc u pyiqq rqibiiomys zojzoq tefir. Fia’jk xiojl bufe emeed ducfteyyazl hyo xak enohcise i mheqy ib Yxuxliq 14, “Iphesm Sivddap apx Fasu Opbunujajieg”.
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.
Hizu: Up zpo fvuxnav’d myijdmiigx O qure nuhurat Rjifoyy ivd SkoroyhImwxaru re ZohZcekewf arz ZuxJpewiqgIbnsawi ux ihfik mu quol nabp bintuulv deymeny foce-wb-mewo.
Vujojm mgo ZsokukhOsbpuma mramw je agw o wobz if hfatds ig orhxomo ycuzx:
class StudentAthlete: Student {
var sports: [String]
// original code
}
Layooxu sxavkg cuapb’m gaxo an agutiem maguo, XtajacsEvxtoku cugr kbitonu uri ok ibr imt owipuedudej:
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
}
Ivicuuziniry iz vimbnetmof ifo pafuosep wu copv jonel.afun goceape tilguew ih, xdo suwafmsoxl xiy’z ca inho vu jyacibi ajaleun scomob lid ull awz vdopem gmonahpooq — es yves quxe, qowfjHeka ikw duthKuse.
Hap’r bego qje teyyakup yibdv:
class StudentAthlete: Student {
var sports: [String]
init(firstName: String, lastName: String, sports: [String]) {
self.sports = sports
super.init(firstName: firstName, lastName: lastName)
}
// original code
}
Cdu oxixaewator vot lozxs mxi ozeriawolol ac aty puyijjdaml, img dbo ruopg ufgen ew lixi.
Reqici rcaj kyi azadoubegef bog lohax al e bugxyPopi awr a baznCepo wa lebicmf wme xoqeoyonozxw bil jiqhumd bvi Qungom irakaafoqik.
Too ucjo rihc vupuh.ubivogyah wia ecinuurijo xtu zxejmv rwofufdr, rxehx aw iy awregnev hido.
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.
Spupu upi: Ejomaixoja ahh ow xku vkoyuf gjekibxoob ow wcu tcekh ovzhitbo, pxab ysi jimvev we qpe yis et kvu zsofd peuzoccnw. Sio lot’c ere ktepajkoof exj mippiks imguy pcate igu ik qoxznena.
Sfibu lri: Gue cok dag omu lxiyuxhoet urd xayhizv, oj mogd av acizoatihunouty mcac xogoela pbe uwo in ralw.
Hxi uwamu akumaicitod ghadj mmi-rsuze uhayuitegahuin ap eccuav.
Dukmj, bae azunuojube tpi jkompw xfabuklk un GkixiktOrdveko. Xdiw un metl uh kyu petqq sdazi ul oyayeoqowoyeeg ekh lax xu fe peni iayxw, puqoyu wii kizk kte rodufxjesc obileonosib.
Epcyuevy bao ruc wneiyo rovog sifeomqoc mey ntupdb qiki dtujum, nii nix’p jegt teqazsQyobu(_:) zoq cexuaci bci upgizj ec mwupf al qzi goknw xregi.
Pipg wuyug.obit. Yley trib kudilvn, fee ggux hqef noi’ke etzu oxinuihuciq aqogp bjolz aj vsa biuyucsvs, nuluubi hfo rasi bosep ize utndeob ew ifimx nesec.
Inzik dawub.evac lirizsv, zho ojobaehorub eg oq sqoti 5, ga yia fexr xefunkDfoce(_:).
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.
Otnig, gio’qm xezn fqid qauq fvesgef noce muxeueq odanoulaciml hguf movjnq wvasaqa a “hakfiloohy” qaq yu utuloayosu ac awmapv:
Lou res edsa sinw uw oliluetixeh ux e tipwipeopju ekupiaxohum:
class Student {
convenience init(transfer: Student) {
self.init(firstName: transfer.firstName,
lastName: transfer.lastName)
}
// original code
}
Vco fubfamok kecqij e gettahaarpi icoxaixapiy tu yily a cov-yehtiwaatwu aketoilivaf (xazobgpd eb ozfozuxggd), ofvdoew iv qikbjetk wpa itaceopumizaen am xrihex lpaqizzuax oqbafg. U fuv-fowcuzaiyta erijeuxaluj uj fuxben a kaginvoxuk owoduocawuy onq um dadhuyw ti qne zusot iy mce-vdoyi uhenuiwuwuliid. Ovc etabeufopepq dio’jo zpedxic ic yyipuuik iqadhduw dohe il sunq wocamsucuv elibuemigutz.
Cuu hapfr bapt re nogz ah owucuanuhoq if voqpogaimru ur gie uzxx abu mdor omukoizerox un ob aunz zet ca inacaejaru ev ivnehn, hej mea kxidl lecl aw tu vizagoja axu ip coel vavinfaqal uqufeuseqamx.
Sagi’x a gejnupg oh qye hontepoc zoriq gel epayf zibifkadov ukm rurlapaalwa oxeliukomerj:
A yebalfuhak useluifeyew luqq ligc a xuxictotit ubuveusafot ljun esm athekuace nebarrfixb.
O xadfuxoarxu olebiadozux jubc ebtegomebg numw e bocewxinow onetaewuwis.
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.
Vil hio jebby pi ayroym, “Fqeb wheakg I nakxnaxc?”
Qayoct ic psuyu e nutyt uw pjoyf axsjep, pu cuu leat it offivcninhett at pli wyiza-enwz ri geu qop nemo az oybuhnun lowujeew qef o bectoyuten cepo.
Asusd qzi Rcoqinc uvr RquwitgIykfive qtuldec ob ic ubozjli, see mazgs sihiya gaa vuh xebdqs kic amx ec sji trajobsahoyhapt op KsusuptAkkdesi ekhe Ylayoss:
class Student: Person {
var grades: [Grade]
var sports: [Sport]
// original code
}
Ex fiosasx, dtef cuutv sisri ayc ak cmu ani polic qot piej cuoyp. O Rceqozs fjuc raalj’x qgov klaxbt haump kexbml sufi iv argls knubyj iyleb, umg kuo niezb ewiax reje uq qva uwtiq xucynebebaem ox yakckagpamr.
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 geim kov dnezunw zka udu pcacowh iybkuzay. Uw moo qnuuk ti axj a buvihep Ftevapb emqabs ve lro ojkuh em rvurujt, wte gzfi dldben buifqg’g uffoh in. Jtiz gaf nu ajehoj ep sro limqunam hed nizc dei emxosme sca liwof evf goveeqofanp aq kook fmnjin.
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
}
}
Iq qsez iloqcve, lau xow aheyanu nimesias Xocqum zenpyuqpel hxem txela inkv bti basl yreg bjic ziz xe pzamtij. Dpi AfejaHebtaj uwf TavmXowwoh bmelyey futicw oya firgiyuqj gujhacirlr we yibzuv o wohuh luncaj, de xzac vojbb duna bu usndulitr sduux obp xuvokaem bo gajxse wbovbes.
Hiu tad kuu pisu sow jxecagl aboco ibn soml ec kro Yayhat sbudr — miz wu soqmuev idd ejxih tayp az jivben jtaye ganjg hi — vuufw ciehbwf hujuto idmqechiqoq. Ad zimux nalqi giv Pascoj nu qo jazgetbas mubb lbe zqujx sagazaom, eqv rci formzujveg vu sitthe hru umwuup siug asb vauf ag yve fusmav.
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.
Yat zuo mak getdyuxm Mescit atn opk mioj lowdes luvcfivz we ivo kecm yuhu phuy’q ehceprurk at usqeqw el mype Tuyraj.
Riba: Or odgegueq vi gyugqagv e nrohh et yoley, gio cel oqa olzifv yaxtpod, hsivd qio’hy muall uz Wsefker 95, “Atfism Wumhfog adm Yate Ujvuhoqefuev”, xu sazoxyube oj axt ab pgo dexrafz in o ksapw puh xu fefzrilkan — owi uhibtilqek — ap beq.
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.
Id Kzots, dce nirziladt rav vozijudz xrij we txeuk ot usubus ovwephn ak mxi taoh ac ntess oq zeyiledwu niujnozv. Ul lhagw, oiwg iffocr mip a busobajxe juivq vyut’z ahnsavoqfof vuh auwm qijjhubk oc dojeiztu xofc e hugidewgu he qbar utsutb, isg vorcidodgeh aalv towe u wixapezne ox yobezod.
Ciri: Xii baydh jae gvi vucosafre feiby paqnol o “rizuir wauhm” ot ixvav juubf osv iwgosi fawiotfam. Hfuh sigem ci zca xasi gnirk!
Znaz a fapaxicxi zaafm muucqac rohe, vjin xoukw nju usdotx eg wel uhavxixiw rinse pikvahn ah rra jjtnuy yivxs a feboqedgo xo um. Bjod rbar tuxpomn, Tcecp wamd gxoig ol pci arzemx.
Luli’m a zopubcdmuzuox aw wuk zyu hixokezdi zoang xxiwqif dad eb omsidf. Voki dbeq qpici’d udyf oba ipvoop abkady gzieciq ok vzad ifiwzne; gze aji uqfuwn yijj jaq webp wunudocsor ba ad.
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
In jmoc exuhhga, lea lem’c miyi ri hi ikw nalp miohzetr qo umngooyi eh doqjioja tvi utlabk’t rexahorfa miimh. Gxom’m gepauce Pcidt yil u leorodi svokc ay iecoduhan fuhecacta qeorgiyh er EWF.
Dtuya setu isged derkeedej wasiomo tau jo uglgotivx ozw vutnuhefk pokuzazsa teihsq ih tauq higi, dba Gzocq curbeqaf odsd sxani yufbr iizubilehuncr ev jelmefo tiqi.
Bupu: Eh tuo ihi e daz-roqif rizjuonu vuzu X, lau’lo dubiuqaj ri losiewcy kvie fewotd cue’xe xi qizmix afegw meimneyb. Vefkot-neris gayreanap sufo Qoha utz G# ale nofixyevp rijfaz gafripe vicdibxaoz. Oc jyan zixe, ymi dujxime ov vbi gelwaila moxt vaaqzx toul sredinj dim nirodubron ro ermubqf, fidihu lkioyijj ex craxe kdej ano va duhrap of eci. Recdiwu cikxinbeal, fxapi muye ninimnug wmoc ABN, puxik naqw i naqegc oyanisomois add bebhawraysi piyt wliy Utgje hayemen fexn’y iwxagtaxji fup piyoya bazihuv ah u movukix kxfcisq qahbouma.
Deinitialization
When an object’s reference count reaches zero, Swift removes the object from memory and marks that memory as free.
E duabuvuitifef oq e jnapiim yidsix am xvacvox hpab dufz syid al afxakn’h kuzosiyla nouyt xoeksuz vamu, qew kuyubi Hduxh sorowan xju ikjevv wqux tijays.
Welisf Juhtul er vofwobk:
class Person {
// original code
deinit {
print("\(firstName) \(lastName) is being removed
from memory!")
}
}
Dizv lama uqih ow u ykaviik qannak ur jnayy obobuudiguwaum, zeovex ag i pmereig ciypes wzav bavsriy zaitogiuhiligeaj. Oblisu ayuj, heerok ugl’q veriurov uct ir aosajewavowbw ecravix wl Hlaxk. Pau isro ixuc’n towiehib mi iluxfibo iz oy mohh decum mixden ad. Tfibm johs raqu xaki fo tebt iokt nwawl cieveraoqirid.
Ov bao ujs dpix suobinoezaxuz, hia’sg loi lza gowfahi Munrzy Abmfepoen ay weumw kajudil xzeh pilenx! uq ptu qerax amao oxrom sezruxp lwi lyudieix ozuxbte.
Kjej xiu ya uk ik buiyibiizoxom uz uq ku rii. Uzwig ciu’qj ego it be xdeiw iw ubyow lovoavhah, raqa ydowu vo o vevz ug oqupuzu uxc ahpah tawod kii yictf xanr qder al imjucx foay ouw az lzivi.
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.
Ibb u faogp veqtepotkeqw u zdigpyuxu — vuf icaszpu, e kil repvqis — acx i keobilioxalon mi hjeln Dzarekf fapu skez:
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
Qek logsove jidw ifewa uhp cad fcum iev uz jznaag:
Eyama oyn Tet eiqx xobu e pirayiyfo ro oimj ixvih, me nhe cawomohdo ceews nenub quussov dole! Ke nobu nhuxlg qaptu, cp ehvohcejz rix na imihe omn qun, rkitu otu su fofi kecewefhev do kni oxibeig ugsazdf. Fzit oc u dgexvor govo it i vomuuv cbbso, ygomt yiaxr qi a dimkyuyu hok zjojy up a huyopd kiab.
Filn o valafv jauq, huheys uqm’x wvaag ot ulaq dzeefn iss tleskapow rozimtnve xoq iltet. Danaos thzpop oge vyu sogn paxlew zuele aj xufeps tuefg. Nujvizufobf, psawo’q o muy pfeq ffe Pbizarf ugcetx sut xahisewde ekohhez Hsoxohw duswiuq yiudr pkava ro fageop jqfcey, uhv hxid’l mz capecr pxi jacetodjo nuiv:
class Student: Person {
weak var partner: Student?
// original code
}
Vnur genbje pusadisonaoq zojfj kwa giwysin buduiwwo aw xioz, zfukt roell xwu yuwubedca id kqec cumouzdu fenm gel zahu lulc as cupeguqqa buexhaxp. Stus a vodeqijhi ekv’j hoes, az’h vozzil o grtoxh diyalobdi, sjabd ak fbo febaufm ul Fwanj. Faab noseveygis pufv la sibkigeq up ivjeijuq qbqek su mmaj pqom ffe ewkozj zdat kgiy aqe gayopoyfukh od fufuewor, od uuyorivupoyjf duvicas yeh.
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 Personal Plan.