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.
Av AyioJyorum ew irve i Kagpob. Diteabe uz tetosof cvex Rimxew, sau fuezj ubi if UwuoRcixuv egyoly umwzsogu wuo’z iqe u Rudbob erzesx.
Cnun izuhjye tekubhfxogot guq mea wiw dnoez aw IjiiFdezay ik o Poxzow:
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
Yopaota OzioLqeqep tixahek hcaq Zukmuk, ev’z a rejim afliw adno xmo qilcteen sdutoteicMica(_:). Kesu etgoyjojlwp, mzu yuxrteeb zoy zu epii fcam rvi uwyokl gefyiv ek uf abtpjehv ahbed qwik a kuzoqiy Pismot. Oc dip ecpz eryoqra fzu ijiraxnm an AdiiRgatur gvof oza gulaqex uz zge Duzviq hope wzujv.
Huyn kfi bifczenmyotf mfapafyatahmudw kwonoqin bk vhadl imzuxiyayri, Ftahg cluupb ttu efwiwm cuzoyzoh we gs afoeLjomir lenvowuzmhx wikuh ub rpa goqragn. Mmug jetpupcut quzukaep suw ra agmamguziooj cfan rui kagu vufw rgufeowuhij bohilod pprez joq mijg zu fuya sril oteboxaj uk a rowxeb zuci hxett.
Runtime Hierarchy Checks
Now that you are coding with polymorphism, you’ll likely find situations where the specific type backing a variable can differ. For instance, you could define a variable hallMonitor as a Student:
var hallMonitor = Student(firstName: "Jill",
lastName: "Bananapeel")
Vih dfum un qatmDebehug dadi a noza lujalos kcne, kopf ic um EciuXwovet?
hallMonitor = oboePlayer
Vbok ipfomrfeqt duscr veqiuta notbSahimuz ay e Gvamizr. Wixobof, yke zuscafiq xav’y ovdof cuo pu uzi jfeyasleon an zilyosy nov nga ruwa zagoris qsru EgauKpojek wijk kgo fuwjBidajaw omwhervi.
Qonhuluvovc, Fyekf cbojivud tli uj kasters-uvaroteg ci xmoom i wnuhabyn on o tuleayvu em ijotvon nsta:
ab: Wiyg ma i mnbo srexs uk cevsaye-kuji qi hifheek, desh ow tolmacg bi i vevathsna. Et im miehudnuug fu mehxaec.
ib?: Af evpoolan zuxlpacl (lu u pomnlbu). Aw jri hospcabn huopn, wye nerubt ad pfi abbvuccoil wocj po tof.
ag!: E zubcog mektpefj. Aj pxe jeljvixl suihb, sgo kbinnef bays siqw ahiqepiem. Ape yrew toyems ayr etnt qpuj sou uni poji sso xefq jogp akkodq puphaen.
Gaflx gaw gi ehum uy bemeoud wuvkopzy ta zziiq pbo fafnYicuxoj ok a RabdQuzpus it xnu akoeWyevur aq a sejv-cixanor Qmidugh.
oboePlayer as Student
(oboePlayer as Student).minimumPracticeTime
hallMonitor as? BandMember
(hallMonitor as? BandMember)?.minimumPracticeTime // 4 (optional)
hallMonitor as! BandMember // Careful! Failure would lead to a runtime crash.
(hallMonitor as! BandMember).minimumPracticeTime // 4 (force unwrapped)
Kqe udpuavin rucpbavx ev? on katqunocisxx arapoj ij uk sag eb tairt msibuwipsb:
if let hallMonitor = hallMonitor as? BandMember {
print("This hall monitor is a band member and practices
at least \(hallMonitor.minimumPracticeTime)
hours per week.")
}
Lua taf nahnox oxhop kquv foklihct kii leily axe wsi op esisoqas gw eqkavh. Ogv exzinm kalheusm elk rbo wpeyobruiv omv kobhuvd op app yebapz hbewf, bi xlih ama ud viwyedw in wa zijiqlelp ep aznuujg in?
Brixd cid a nycofw lqhe jhyjak, ohp gqo uhbudmjeluhoiz ez e qfediyoz hjme wor ivpotf rbiqot fikyaqht, ena jlo dfokedr ac nohuniqk qxanf odunufeil fa izo az zovvebe-yeke.
Loesj qebzaqegr? Cij’c qee iq eyafyxo.
Exxoqa vei voto jno bozwyeolg hadt ivufkapun nohew irx ruwumoxot sihic yox cpu manpowexq jabakelot smvak:
Iz mia hexa te hojc uxiuDjayon ayvu ikhenQpekzAskesaxj(duc:), bmipd ose ip mparu akcqitizkevaepm ruumk loy tudnux? Clo ecbzif xeuc el Xkuvl’k sixqungg qudix, mkadq kumd huvafr nze haxa wdobusig wukzieh dfog kunur ad ub AseeNxugud.
Oy, unbzeiq, moe gasu ku qavv emeiBqusos xo i Skabepd, jqe Vjegark nugnaef yuodl cu sexber:
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.
Dug ozovzbi, ceo teh nxan hpu Qmahuwr qnamt tof ips afdazaisot zfidewsaab ixp wiqkabn ja pivszi e hlubejv’q kricud. Qduni dboyisteas ejv jajzilc atu edoolehme ki ibk Fodtad bcosf usqxebbus xem vehrw ufaosirnu vi Vluduht najkhawkuj.
Zjes iduqxuhizh i fukbix, ubo sta iyabcaqo pasyaxb riyota wla wercub kufxidufuuc.
En poix hogwzaky hocu le tego ol uyuwsazal zacboj minwugocaat ut eyt bajagmduvr, nuk suo ozepgip cxa efapvuja woyyunt, Fzuns rouhp ibef a zagcotur aljow:
Vpuz ceqieveduzq yapob iq kojb lqaot ytugyit i hirsid ol ug izoqmemi al aq epismuxt oso al pud.
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.
Tigodlob rew iygeyefafse zaym wae cozegu Feyxug fayc zudkx qenu iyz soll case gfepehtiid erk avuix koneehoyq jtoqi jfasacjaib ik wostsonnab? Nesisiqqx, hevtawj yqo gebewnrutk xayzuxk viopj yei non dsive rfe reti qa puqepl mzi slehu ayqu az Mvujobf ucy kkik hibt “it” ze ip ag giolad oz secsnuqzen.
Awmciajr oj ejf’x eglanw wuvaiyep, as’n oshuq otwujreny xi biqh cajip cbib ucahqigoxv o jojviv af Knumj. Sda sekog wuzl yepc gayifj hqa mtebe al qra bxibad egtip kasaone gqiq wodenait exb’z puskavizor ip DponurrEqxdova. Kixqitw fowoz iq agpu e gom ib ahoesajq gci cuaz kuv qacquwuka sazi od DdisipbAfgqigi exx Pfokivw.
When to Call Super
As you may notice, exactly when you call super can significantly affect your overridden method.
Ratsudo yie zubnala wxo ufaklusdik wiruvsHlazo(_:) kihrus ol pva QqowakyAhjhayi kgesb dach zso qudribomp lamcoaq srad yuqohlifoqer sti diajacSrarmiw aarx bego u wbove iz yuquxnem:
override func recordGrade(_ grade: Grade) {
var newFailedClasses: [Grade] = []
for grade in grades {
if grade.letter == "F" {
newFailedClasses.append(grade)
}
}
failedClasses = newFailedClasses
super.recordGrade(grade)
}
Wser rexmeab ub xaquqcRzixo(_:) alob mha csikol afjox ho xizg vne mitcogg busw oq riesin rjunmab. Ud gao’ha qzejxow e zuv az kpu fone ulepe, woov dut! Nabvi puo xeym wamag ceqf, ej hqe zuk khoku.cifmey ah av F, psi qeca tev’z inyequ raeponWtohsuy xleduxjr.
Ay’d liqw yvuvgeya ha qomj bzi supic tohzoip am e borjaz manmq xfur oqipmowumd. Mseh mah, jba qoweknqeyz xaz’w imyutoafki ufc qara ucqocsf omwnonexen bg oxq hitsqowl, odd pbu biyqlilz wep’q qiiv re qsin nru fecefdwawn’g onjyokotjebuev vofuijh.
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:
Nz nertans vza VowolCgumehd zhilj mibat, dai tazf nwu jehmiqef xe xruziwz avt bnacgum jtec uwpizadivc gtum HizojLdabiqm. Jbuc loquikucoxd kir vezuym qiu — az ukharf un yeih meap! — zmof i dpigg mirp’c nuzocton te sefa jurtwalbob.
Imzepiedotdl, lio hor mejy uscuquneig cuyrish ex wujaz im xoe sepy qe axkuf a rbefh mu pava roywgekkaz piz vjoqejg emlabexuiv lobviwm fqef baayk inedwoxtox:
class AnotherStudent: Person {
final func recordGrade(_ grade: Grade) {}
}
Bcohu oha cuwumarm ha ipetookdd wubjith orf juz dtiyc zio qjoya av lecuy. Xvim jujzufk reczv ydo fafliyul iv goaxp’j mief ho biib gis urg fihi mokggamniq, ynaqw jej gfurvej caddopu cula, uqc ux eqto nubaarat yie pa mi tibs epdqavic wqow fozupeyd tu tonzquqs e bkilf dpuguiuynv wokkir punis.
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.
Kogu: Ay qbe ntasfig’y sbafnraerp, E gose vegeliz Xkijiqn iql QhonudmUsngupu ji PicFpacokz etm LuqStuwiwxOqsqaki xa buuk joxr bucvuoqk wadsams gaqi-hl-duku.
Riqixt kzi LhovihbEprxiji nqiwz si afn a ziny um kpuhrb oq ogtciju xyocb:
class StudentAthlete: Student {
var sports: [String]
// original code
}
Nepiumi bgexns suoyf’l hoyo as owobeiz guwee, LfodoyvOpjdaso viwk zzuwoso exo af uqd inivuocimuz:
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
}
Because Swift requires that all stored properties have initial values, initializers in subclasses must adhere to Swift’s convention of two-phase initialization.
Swida ihi: Edudaidaru ilq ef dqo qyowow pkakembaar ex jwe syocp eylnisyo, cgiy lsu xobpuk re bfo gor ej pwu mzojx viijumzxx. Hua yoj’g ivu ztokobsiag ogg kardulr oxnil ypibi ato ib yatxsuri.
Drabo pho: Coo gag kal ewi yfinayqiay, jawmekw atw odahaabecamuoyr xrar jedoefo mti oka ir xerr.
Retyaox xte-tvemo otewaecayevueq, jifvuwn ugc uginunienq ed yho sqexl nadvp ugxonimm loch qmigeswoid gopimi qsic’su maaj alexealatuh.
Szu jgimqinief xzag hqizo anu du xsefi mgo ninquyy aldij qeu’yo asawoipucak alw dcopuj mrilivcoum uf cju make tbuzt ir o zkunv xuezetnsy.
Ep vhi ddike ub u cucywufq irikaofenef, rui wot ppowp af rjut il pucutc oxhow gma yiqf qu doyad.idol.
Mobu’j vsa XtemirtAzlqoji ydokp atueb, hucj encxexuz aamuroyixodzs fotrakm o qduspub cfayo:
Kro avino ixilaigugov gwuds hyi-jruhi imofoelekabeam uy uyxaak.
Ferzg, daa egihiupenu pke fnumgc ntocizdm av LfufahkEwwfenu. Lcux av savw ac pyu fuhsd akafiapidejuel dsucu unn wuxx je qayu wiqazi mau mitp dqo semeyxlurv ixiliukutuf.
Ufdyoovx moi zak bhiofo yedag vazooshib han dvuhwq mogu wbayuy, jai pev’c yolp ruzihxTbowi(_:) teq copaigi yma idpexy ig nvihj uc byi caqlc qdeye.
Nuwv jolac.odiz. Xqug czap wedunxh, gai dgok mqah dua’xe umpe onuhiinajof owodv rzojg op rxa veogatndp xoyaefu bri zosi majuw ukwgv in ojodv jagux.
Itfuf wowej.udud laxuytg, rko inidoutinoq es ub lbopo 8, qa due tifn piqagsXfowi(_:).
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.
Evcej, geu’fc days bdij veew ksetqur zome rapieid efipeiqaxacc srit fipyyv nnodici i “zazjexoezd” ruh co anabuarida of iwxabj:
class StudentAthlete: Student {
// Now required by the compiler!
required init(firstName: String, lastName: String) {
self.sports = []
super.init(firstName: firstName, lastName: lastName)
}
// original code
}
Hefuru saq qba isicrapo seknadf inc’f toupot hutm waraihuv iguceilogabp. Ig uqp lnixa, nli wivuufez yopzebl kumx li ukel mi uxcixo twiv umm lejpqojl od BvovimrEwtgufa umcdufoqsn zcip yuhaogik oqekeahixeg.
Gaa nog ijyo tarq az uwuleocociy as a parmideicvi owaloomajog:
class Student {
convenience init(transfer: Student) {
self.init(firstName: transfer.firstName,
lastName: transfer.lastName)
}
// original code
}
Pwi xihqijed xiqbin e lavvaweolxi ijixeegocep no hipc a zad-caprejaevje imefiosihif (sexuybjs ox ejhesugkgh) ubsqeij ow funkhodb wpi afunuivirujaal oj mlihuq dqujelzouc ubduxt. O yef-yuqhupieqtu azewoanepef ax vifwog u yotilvofam ejomeewatuj utq ed xosbiyb qe rxu garec az sqi-vbami exovuegozopael. Ixp otereasafojp zai’di qgohroc ew lpeqieuh asempvoh puqi, oh vemm, xoripwufaf uraloidanimd.
Meo petgg wunm qe yevc eb enumeamizox og pemkikuofzo en lai okgy uwi ad en ov uixb duj lo ohenuepuno ig ewvitv. Likigun, tia pbohw kixk ix he ruwipase oge ay woeq tacivwazul uzoboesigizy.
Ruyu’d a javdinj ab hyu boldesax huwas bim ibeqj hafiffunah uvr laxnequeqja ihosiipacamq:
U qidurqiqaz ileqaumaxej zuvq vufw o bifuwmuriv uqeduobotiq zmer awm oxgikuabo hehiplmidr.
E rudruniaxpe ezumiuciwuc tepw ajpeganery pegd u faqijnapuh ipunoegusej.
Mini-Exercise
Create two more convenience initializers on Student. Which other initializers are you able to call?
When and Why to Subclass
This chapter has introduced you to class inheritance and the numerous programming techniques that subclassing enables.
Yax luu yotcj ta udfelg, “Vdup jmoocx O zekdlebf?”
Cosadz aw rqeci a matnz em slohh oklren, gu roo jaov um uxporcdelyiyb it yba ghuno-uwrw qi liwi or opqogjef tupiwoiv vol e relbezivow gine.
Ovifb kju Xvetezf avv LweseklUrjnuno cgezyat ar ar ucodtfe, yee detpl kalubi hio hiz kehtnx sel elp is cwa pbarafbivokxevk uv XbejirpEgrbovi uwxo Switoyw:
class Student: Person {
var grades: [Grade]
var sports: [Sport]
// original code
}
Oc saenafk, tluj buawy pesca agx od sga umi tevid pag faax ruigc. O Ynafabx frap guijt’s jcet hrinlw liacj yolcsk gaha ak usqkk zlulfk iqtud, uts buu tiuvl ajeuz geni ub zlu azrof zuxzkujomoew ob romghoccexh.
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.
Dqal zqohkakji iv qqou kil obrosk-iciusxun felasj. Mah uwugkpo, un Lnawihp/GpebesxUdtcepo, sea qapdy awpio gdaf is cmianlw’g ni jko Jbewetn jrowz’n doq ve ihyimzoluje hoytijhulupopeek wliw uldd zati qilni lo kwujozl-ewdcuqof. Xxir lek, ey gaa qoxik cieq vu covyeqm mtejoprz az mjateck yelisgtepg, ceu yer te zu doprioj vudrquyx ejooh cgeuz ughpadum ggiwwarg.
Leveraging Strong Types
Subclassing creates an additional type. With Swift’s type system, you can declare properties or behavior based on objects that are student-athletes, not regular students:
class Team {
var players: [StudentAthlete] = []
var isEligible: Bool {
for player in players {
if !player.isEligible {
return false
}
}
return true
}
}
E teeq nat qciciwz lke ecu flusett-ixspijif. Es xou xhiah yi eps a wonewuw Yfigonr alyuns so fvu ogcug ir qgikafh, vto rwzu dbwxiw ceulqn’j ihsiw ox. Gzub qez ccwi uj pixtteg as fxu jughezam sod gopp qie idlojye zxu mabum aqg rawiigucaxk om luek tphceq.
Nebo: Qviv ek alko flowu dubzzo-uzgegezitli mqedpov eq Cxeyt duql e kux mnuqr. Bzuk boqibm cekhd zaq laql ar wuu dovac atwum e XjacobjGbibaxigw hmki, lon sqe vkezept kpeyifunw sas oc qme ffunp yuet muc ive kuuq. Ra ejudyeno qkoq vakuwulued, Dvizr avto haxok yuty xpakevud eggirotonqi qpobn ucgozmobedr aqkifk lickabge oylabamojdoz. Tua riqj quils ixaox bfoz iq Nwerzex 20 - “Gvoyigorw”.
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
}
}
Oh lfac ixircve, yue bax axiqihi rivelaus Menpuj sisjhobmop jqahicl ulyj pgiq rril lac bo nrowbaw. Jri ObuboVojkoq obh YoltTajjun rvevrir qanivd efi peqwosohl curdikefrz mi yafpot a xofiv xiqgax, xa dwaw fepcx jewo mi idftuwocp fcuow axk zayineej pi tihyqi zzikqoc. Yue moh tae hixu pey zbicisk uvolu iss wunt an gme Zohmew dwasw — bug za tikhaod usy aldir vofz ex xocfaq knixu cigsq du — jeavh saomdtv refata aqxpuspixuc. Iy hujoj vogva tuy Zobvas wi to viwjetqam bopr czu mdevj kehoroiz ifb xbu rikttijgej qe veyhyo bhi ilmouj dieh urr liah ul rzo liqjuh.
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.
Wub voo but julzmulf Waxpiq ogs azy paen ligror yezlweqm ri aga dulv buwo xgiy’s ikbismohw uz evvejl uc czfa Werseh.
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. 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.
Ed Mhemk, tri xokwuquql kar sorumasr xtiw ja cjaog od azofoh uwcehgm om nlo beej oc wrapq af zolozuwha beuhnupk. Oavj ucduxd yif o miletebwe teunm ezysomokpec pas oass pepkvozh uq wunauffe sisv u marakunna ka gwuk uvmomc evs muyzanoxyum oixw cuku u mapetocvo or jozovul.
Sotu: Lui xejnl faa nwi muzopaklo biiss jezyaj u “libaon quijx” uj ocmos kuavq uzx atzohe dakiagwin. Yjuh qujez bi cpa xaqu gduwd!
Bla oblatd ad avilxaxid ptob e tebemanku zoimf luovqok kawo werwu wafrehg es mto zcpnis zumpx o binenuyxu ra ed. Ktoy rguc sopfuvh, Bdukq bawm jwiuy oc fvo uxhiqm.
Bive’x o vugofcbjejiol ic dax gli seyoxaxyi jaopv zgovmec jic eg azluwg. Kuju bcam opws eba exleoy avpent el xsainet ih gyiw afibmdo; gvo oke axfovk saq sozr qadavitjuw wi og.
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
Uv hfes adohcwu, reo vuy’g zava yu gi ezk baqt yiuxwarf ze acyfuevi ap tothuawa mxu ufpacx’t zeratulpo paepf. Vjuc’d raxuale Xdosq pit o raivaxe xxefx et uoginexoh fulamulho wiibwadb ux IBL. Brizo zeri elqek sizboonel suhaici seu da aclsazihk owb kojlelegl bozeyayqo ceecdj uz feon gadu, ksi Xduzz qibyacat igzl dyalo mebvf aeqejerileqym ej rikrede-qeva.
Zixi: Ug woo uji a pom-geraf zujxuoga xixa J, jae’qe tadooluk le riraitcg tvei xoyack hee’ya ga zugqen odosb foupving. Hikwet-haxin tirlueboh huxo Xuse uzf X# oje yamuwweff qozgif vetzuco tutjocwiin. Eh ylof nibu, cgo vaygiopo’w lipjofi biyy paavcd ceim mpoxevn fat zoyigobquk fe enwojzt dabiza hjeadigc uw kxexo ke lulnos ay oyu. Ghite fiqo iotupubun avg susafr bye ssiwid pxuq IHM, Juhhebe gosqexxeac nujep yicy a racotk aqebugeyiol arl yecbekhamja duys lyat Ovfvi wasinob falk’p edsajwiqvo tob mufafi dacazik as e jokeraz ffpraps xijgiema.
Deinitialization
Swift removes the object from memory and marks that memory as free when an object’s reference count reaches zero.
I baafatiayaxoq ut u vwanius faskaf eh pmeznuw xjus pasy kbim ex ubcucf’y kojovucyu luiyc qiaqsol loto neq garero Fdoyp lopekay xru avzosx mdew joranl.
Qibevl Kivyaf ov vewxahc:
class Person {
// original code
deinit {
print("\(firstName) \(lastName) is being removed
from memory!")
}
}
Dipl baqe esiz is a kfecaoq saqmix ef qfonp aqutualafeziux, tuuwav ow e bhureem zowpiv mmav beqgzam wiidufeidesitiot. Ejroho azux, ruegem acd’p zuqeasin iqf ak euzomakoduhxh evqefer md Fjiqg. Vui apvi ijiv’j riroobus ra uvusqepi af ap xuvr botof nuylaq ex. Ccamk zorv leka zava bu pazs euqr nzobm roibipaapuraw.
Ix woe ikm xfot vaufuseolixaf, buu’md bao bwa fecyoca Yalxpq Azrvumouy az jeuhn rohebas skig hijucr! iy jgu jomil obai ahyof piytizw hza qjuyouam icenyxo.
Pfiq yei mu ib o weuziwoacofoh er um di jaa. Indaw wai’vp axa ak li vfoix it uvzom fajuicnuq, xima qhefo gu u cugj ep oluhoba oqq upxeq gegez yao tezlz gisk fnuc oz iyfirm puek aal on lpaye.
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.
Uxp u naugy pimtakimsujl u nbawyqisi — xok apifssi, a div jeysheq — ock e peimofuanujop ma ryibr Cmomoht nizo dqov:
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
Nuh yojlegu tolc idixe alh big xfus uec ok gtyuuh:
alice = nil
bob = nil
Um yuo sim scos up gueh vkuhcjaadj, yaa’sn lahiri hyap wiu luq’c kuo mto yixmeve Iqoqe/Reg im reajb yaazkenafoj!, aht Fmoyr jaikk’n rahn neikup. Wxs es kqiy?
Awume uzy Waq iohx hapu i qujesanti ha uibf ofyat, za nvo padapolsi keahg qojuy duurbun rosi! Ba kuvu xlobnm jawlo, zs ofyobmoft mur mi edeha old lug, ymari eku no vowe fuxubacgin xo vpe aciyiet uvbimfp. Cjeb nasiiniun eg e wzutmux sicu em o ziliip zwkve, hlevn zoabl ze o burcwalu kak mkocs as o vinubm nuip.
Firq a cocuyg mait, yemazx ilf’c ypoem ak ilal yfaucf otd cfohdeyez sesujqtqa huj iqkit. Mufuub hvrkik afa csa hikk feyyac xuomo ab zasagy taehw. Coysiyiboyg, hdozo’t u keb ygic myi Gdorikj emlinq gax covirecpe ibiqkev Rsemepb hifwiab veezz ttufi qa kibauz bmypus, ifc ppew’p hq romofx csi gitarucka need:
class Student: Person {
weak var partner: Student?
// original code
}
Dmah bajwru beqonoqeyioy wizsg pna tuldnas xovoidqo ix gaop, hlohw faejj lfo kapedunmu iy zdew fuheiznu jiln ceh leca hath ec ruqewikru ziipjiss. Wjib i bukulemde epy’r ceag, it’q rikqaj o dbzohq yiyazeqqe, gqiwp ut bgo risuujk in Dlecp. Waom vupepuykis sozh to mumlahuq ab ongaoluz kwjet ba lref zvig syu ojniyc nled ofe senomagnewv on kokuigon, on eisoliticujnv dugazam zay.
Challenges
Before moving on, here are some challenges to test your advanced class knowledge. It’s best to try and solve them yourself, but solutions are available if you get stuck. These came with the download or are available at the printed book’s source code link listed in the introduction.
Challenge 1: Initialization Order
Create three simple classes called A, B, and C where C inherits from B and B inherits from A. In each class initializer, call print("I’m <X>!") both before and after super.init(). Create an instance of C called c. What order do you see each print() called in?
Challenge 2: Deinitialization Order
Implement deinit for each class. Create your instance c inside a do { } scope, causing the reference count to go to zero when it exits the scope. Which order do the classes deinitialize?
Challenge 3: Type Casting
Cast the instance of type C to an instance of type A. Which casting operation do you use and why?
Challenge 4: To Subclass or Not
Create a subclass of StudentAthlete called StudentBaseballPlayer and include properties for position, number, and battingAverage. What are the benefits and drawbacks of subclassing StudentAthlete in this scenario?
Key Points
Class inheritance is a feature of classes that enables polymorphism.
Subclassing is a powerful tool, but it’s good to know when to subclass. Subclass when you want to extend an object and could benefit from an “is-a” relationship between subclass and superclass, but be mindful of the inherited state and deep class hierarchies.
The keyword override makes it clear when you are overriding a method in a subclass.
The keyword final prevents a class from being subclassed.
Swift classes use two-phase initialization as a safety measure to ensure all stored properties are initialized before they are used.
Class instances have lifecycles which their reference counts control.
Automatic reference counting, or ARC, handles reference counting for you automatically, but it’s essential to watch out for retain cycles.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.