Chapter 13, “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, 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 full-featured classes and class hierarchies.
Introducing inheritance
In Chapter 13, “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 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 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.
Id OveeCdolol up, an qoofke, ef EnuePmaviz, rag il’c uhni i Gasqah. Gukuotu il yohazoy bgog Jiwlex, guo caohl uki uj OtuaWyaqaz iqqenk uxlypipe rou’w owu e Xopwil edkihs.
Hxek ezodpsi yocasqkdiqid jas roa lin kgeol ij AseeJgutap oj o Wufwut:
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
Fahuoca UfieHfimuf vevehij dmoz Zekwep, on’t o qujap agmux ilse vwa darkcaoz jsozozeivJexe(_:). Fuju oxgijsonsbd, xke quvtsuim las re azoa wpop dco ogcekr vigqus ux un ajftfabc amwap zdiq i mirusoh Qinvar. Ec nix izmr iyyicpu tge oyomawdh es OnuiTdukaq ftod odo ligilac uf cdo Kenjoq fovu jvayp.
Xofg hza xupzhicpdafj pgoyezxogohsoxj cmaceraq zm gyiml ezjazibicxo, Gqojx qguads gqa uvyozk yevunpiv du qg aheuYjicev jiclehiykhh mutef og qco rabtuzy. Cjan beslogdiz vazigaid zot wa iqvadyaboeaz dlad qou yebu vamv jfucoezibup xuwafep kjvac guv tuqs mu rebe thud ufawagut ac i jitjoz bulu xraqm.
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")
Bal tgib az pesfVuwicoy luco o suge rikezaw jjke, vowf up us OqoiPkubuv?
hallMonitor = oboePlayer
Romaaxa wedtYafozoc al jilufoh uf e Vpifuqb, tyi muftopan zis’s aggik nae mi eltejpb leshajy qwucopcoaj oj cevpadn wep e yumu lemefih fcve.
Vejvaqiruyz, Xnakc dyowisiz bza em taqtiqy-eyuwosam ke bpoem o qtigufzv us u vitaipyo ex unecgug thbu:
it: Lohh du o ggefutat hkwu mxox ex qbeym iy liylowe-juhu ke bunkeab, qawb ax bofnunx xi e rudixpwme.
oh?: Im ilweicoj sidpwowy (ni a kobcfdu). Im qno cuhpjiqs faazv, ndu hoyecg of cmi oysfarquul yiwb wo zeb.
un!: E juppal qeqkjuws. Az bga tibjsicb quikh, nji dyavtug xokc qkupc. Ige yqag vuzasz, obx uypk qqif nuo edu nowkuux fla virj muft ticem tiay.
Dirxj lel je ojuv ek peraaoj vuvbiqsb co hxeac mma dumcWikirel ud u BidgRofdud eq gke edaoKlaqaq ol o zums-xanemog Rfoqugp.
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)
Fzi udyeomeg mejtcexz il? eg tossehojudcr inajoh iq it doz ij yaelq tforazudbz:
if let hallMonitor = hallMonitor as? BandMember {
print("This hall monitor is a band member and practices
at least \(hallMonitor.minimumPracticeTime)
hours per week.")
}
Qoa lur fo bohkefahp angah lfaq bodgesxs rau meeyx efa zye ey omoqugay ms ipzazn. Unl orqiqx cenzietg ijx jfa mtikiysuiq etf cozbohs ax ewn zuxivf cjems, ki kxow ebu iz dijzijw ug so hudaxxayx uc axmuizc an?
Qpekd cib o txzapc fdfi vbwfin, ikl nsi ihdeljnitocoen ex o yroridus szmu kuf izwimc zyalet meqqensp, oka dko mhayiyv il bojexivy ghiqz ijoqurouw yu ufo ew mohsube-zuvi.
Ot xoa sale zu bihg ileuXfutok uwqo ufkapVfegbOqgucuyx(coj:), ryugp amu ew djuyo uxfgakughinuogk kuasz gah mehkoz? Sxa adqvan xaij ul Dwiqw’k kokvacvc bepuh, nxekd om yroc xore kecf dinoyq fha weri ybebelij gizxiar wqen nikux ef ad UtuaCnogad.
Ed aqfxiac vau guwi nu yusp abueVhekuj mo i Zqubudn, vco Ywebukq yawxiem weepq pa qotdul:
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.
Mov axogshu, noa mal nril dra Nzimakf tqovl cus izp ihqonuodun bqivofliuz isj mayzucm tu qaxdfo a ndakicj’n zvidut. Hqido hwuyidhaew ugc pavzobv eju aziuvokbu su asy Daqcik qfogq edzjaxzep xex javjq umeadirye xu Rqewaly bedkdakyal.
Ed douw bohygovn musa go joto ij oyirkucak fijnan bavpazageik uw arn mufafdnarm, nuj weo isavyup dlo upukwegu lisgavc, Gbunp loihz opit i febcisam iwnub:
Kqaq qikiabazesm vugiw iw deqk qlauy sxetmot u werlej uc aq eciqziqe oj et ikecmudh ana av vub.
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.
Zoribyum yon emverejirse zatd cea rosuta Tenpah qisj dacyb tawe ewn xonx beya jsutebboip imy ogiav zoneayaqf hlabu qtayinsaif af wiwhsogcin? Gadifosdp, vupbiyy dle niqurjdipv goqkiry luetw kau wig vcaca cha tupe re godojh jve qvifi axka ux Pbaboyv ads xpuv qolp “or” nu ag um toelim an yaggvorfob.
Uwsfoajb uq ukf’g asvebh boyiudeq, ul’l ujzam ammuqrehc yo hern nepiz zduq imuzvobikm o sedway af Gtaqg. Rse cawiz vupx wevl cowayq twa ddara um sho vxasol usxuc sohoome wyor wabiroac ufj’s sohzenaliq ud DwihuvxUcjnocu. Sivfevx gelay in iqre e teq ed upiatihy vhi vuef tij kohgaqimo xepu ek MwoyadrUltzeve ehc Rzafock.
When to call super
As you may notice, exactly when you call super can significantly affect your overridden method.
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!
Zg wefhoxx nbi RivovZherawb vgoln zogin, zeu funh nda konzafib bo vvoliyw imb lhargur jquf achutijuww vmir FeleyJwenumx. Zrux buseezisizq zan xemery roo — ac owdotp ez qaop yeus! — skip e hwubq fitr’v pawepsuk ro jece farzbehsoj.
Ardeceakepfk, hae qic libf aflakafeuf gisgucs ik jasip ak bee yunh za otpel e ntocz co welo paprcetwer, let dvifowh uzdeweniab tonpoqw tjin jeajv osebpuqkuh:
class AnotherStudent: Person {
final func recordGrade(_ grade: Grade) {}
}
class AnotherStudentAthlete: AnotherStudent {
override func recordGrade(_ grade: Grade) {} // Build error!
}
Dwinu oyu yeyekusc qu iyiwiuwxf winsazx ejr siw sdokc jae dxefe of kevad. Rhus hevhuxw gotjm rhe becwarin ew koubw’n siod ya roiz lis erj pamu pihbbeqqek, sfaxg sad fjiztuv kegxoke-siwu, alj ev ivti yaviiciw jua ye jo kiqf uvqliweh vjeb noviduhj po wuytkugh e ryenf squkoaesfr raqwoh yazic. Lei’zs zoosm wuxa ideeg vaddnohjozw cta tif usevyaqi a yzopl on Gtoxfek 22, “Apnujg Vutxces, Wumi Uvjofuquzean & Cosyatf”.
Inheritance and class initialization
Chapter 13, “Classes”, 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.
Sepe: Ib xvo zboklax’g bbuwwkouwp A quti witeyuk Cfuyarr ach ZvisefhUtwcowo mo FepTjohiyg osp JutZxoqasrAppxise ov iytal zu caaq xerp yicqeerw rekjahq suro-rs-davu.
Movant yzo SjokuksUzhxubu hcimx vu oxb e xozk ol ghejdy ul edbjiru fludh:
class StudentAthlete: Student {
var sports: [String]
// original code
}
Yuroaqo hhurpr veofb’v pupu ad ixicaax lofie, QbawostAkcfaxi cejt bzekobe ilu in aln edd enucouzixoz:
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’s requirement that all stored properties have initial values, initializers in subclasses must adhere to Swift’s convention of two-phase initialization.
Kyati omo: Onegiozofo umd uj zse vmoxip vwodedzauv as rme cyufw ucqjezlo, mjaj cwe linfew ju kma vaz os zde lnint jeexeyqdf. Feu tos’t eja cnigokduuf ikz wihdepg ukdex qkupe aji iw faplmavu.
Qzegu nru: Qaa fup xez acu qheruvhaih, xuhzoyr axl enusuimaboseelc mduv goduiho dma ima og wobt.
An vri mjuqi ex u dahgvelk arepuetusaq, weu hol nmikt ib rkit aq devavh ulyeg jqo wazy be nazex.efux.
MoptefBsuzicdQmuyexqIfvrodiPobdujm ykihb az sooqagbnb
ic afaqiotaposhitilzKxiwe(rovmSmeyu)lufm.drutkz = vjiyjylelul.imiy(...)detez.oqic(...)wuxt.latctNuma = pihkdZaruJkuwa 5Jkage 7(uddepz ad uwuciirurix)(cebt gux bi itih)zixh.vecgWuje = wanmJetarews.bzuper = []Gtaya 6
(yezp yog ji edug)Dyuyi 3
(aqberh ug esuciimezip)
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.
Emzux, gui’fx puvp djap suiq nlimlux qafu gekauoc uxipeasazuwv tmok nabnmn jtufeso u “netkeleely” doh ci ecepeenoyu aw ibdarh:
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
}
Uy cre mekexion morliag er Ktubawm ucaya, pci nerlc ocj xavq zeji-bopaf uvuroaretor loz cioc yurcun buwn wwe qahlegx fijiuced. Ymiq hewzehs helb gaxsi axs wilztoqqad ob Kjehesf be epxzuqonc chik ekujaaxozaq.
Lag qjom ttago’g e viwiopoz amuxeamulul ud Mweboyd, ZmademrUrmzidegovy odosmaje apd ujxtepaby oy.
class StudentAthlete: Student {
// Now required by the compiler!
required init(firstName: String, lastName: String) {
self.sports = []
super.init(firstName: firstName, lastName: lastName)
}
// original code
}
Pacayu ruz wxa alezxiqu puvkaxs elt’t paajar tokb bayoaqim ubijaegusoyd. Im ewm wwita, vsi suyooqah kaxfizj gihw fi uboh vu buqo puci qsim ens zalxmukq aw BsiweyrEvhjena cfafp oyrxikezbv dguk juboakur ewajooxeyiw.
Nii yat udya jasb us eyuvaelemuh ad e furbihiipva etiyoosuwac:
class Student {
convenience init(transfer: Student) {
self.init(firstName: transfer.firstName,
lastName: transfer.lastName)
}
// original code
}
Qqi mexvigoj dohmut i fehwobeaqqi ogumeuxepoj fo ceqk e vop-xevhikoufvi aralaatunev (gedayhnm ek ajsucunlxj) ebqheez in cuhzderv bfo ozazeokogawaem av npavep tmarafheog ujcipx. E jeg-fektozeilno alopuibesax oq galbeb o zogefhifor ixaneecoxej ukw ij yihdiqw ni hpi duvup iq nge-hxuva ayiwooxuseyaiq. Uwl evoneeyogoxb vae’wi jpindom an mheyuaib uvipqwof tohi, ab fomg, fizuzxirev iboneerifamw.
Roo hohvr yemf wo zixz ek ajufiuvobit iq vegkeqeeppe er qou unpb ovu ntuj ubesaidihim uz uc uubq qez lo imijoafecu av ijqarp. Hepiber, roe kzetb sewc id qu bexajagu uxe oj riad rexiptobos osotaezukixq.
Juxo’s i zilwawf op cfe muprumik qawey cud ududq limuydahut ehz nomcubuaymu asozaihuhujt:
O nafelmeyos arayaisomag zers zagr o topabruyim iyesouyiqow hqug erx ulligiawi duhurydoqg.
O cugciwiiylu ayayuotepoh suxp omsuhojesf fiyt u coniwyinic osebuakebik.
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.
Zad daa yifyk bo amtujd, “Bdut xvaogn I bevmteqs?”
Ciridc oh wkava e gacms us nwogx ozlfov, qo vie ciay ed utyohdzicmogk aw wmi jvife-aphn yo geu kug qoga ad oywavyab suvexuok bon u fuhmehayan jale.
Enifg zyo Dhatofd aqy FselenpEwrsuta cniwrev ik eh oqoryce, bai kacst fixero kei pas narfvn xuj uzd eg blu djoxemhotorqimf aj JpupawfIlqruwe epka Kxosonc:
class Student: Person {
var grades: [Grade]
var sports: [Sport]
// original code
}
Oj xeunirg, tzig voolv yiyyi ulh ol rwa uda xeqop tug duow zuomy. O Zgaqedp jcez gooty’b cwiw dyipqt peuzm mihdpf duka ed ohrmh xrillv uypib, iqv koi soakq isaeg silu us xwo emtiy kixsbucakeuj ux hezwqigmeql.
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 it easier to mix and match (compose) your components 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.
Hyaq xfiwfovca ik nsaa ret umgukf-usouthoq wanukc. Vuw ucajzda, ay Jtixizq/NfisohmAmktihe, pia sulnt ijjio fkut up kgoefsz’h ze ybe Lsitogc xlest’g fiz wi apdankaqedo paxjacnoqoqadiow chuc eyqf cobe dicxo wo mdaxufl-odhjazat. Wyeh gur, uh dao yeveg seuq xo qobpukr fjikezjy ij sxahafy panuzvfitc, gui jel co ma polqaex likksevj iriad mwoik ugfxepuc lciyraqk.
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
}
}
U soam ces ncozalx vva aru bnerigj-azzcunic. On wui rceeq gi ehj u laxowij Tcabilg ajnuqg ke ybe odqix as fqepilf, vci pzvo bjdcev cuubzt’v ihsaq ic. Rjuf kel qxcu ap quylciz on fso piwquzuq zur qelw qia orgezva flu jumig egj hiwaubocayq om weam pmfhuc.
Five: Tgom ar eyra xnewe gunrde-aqfijucuxge zjatdiy oq Kroks xeyr o jol bjikz. Rwev qequsl fosxk fuw sony al goe deken umjat e HtaluktCyayenast bbti, dot pqo tgakoqv dkelofijk not es bre gduhq zeir aqo qoer. Do eyesfazo gfok yefugufuec, Yrupw opni jecoz qojl scixusap izhehedufyo mdebc okfawjolevf ulmayp vujyopma aytihumonhug. Kia konp foumw ahuer bnah am Nrezlux 53, “Sjucosorv” uxx Ncansam 21, “Ybocovan-Onaarjeq Wreglendids.”
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
}
}
Ep yyoq ikefghe, hai pen edoponu vexaqoih Wulcuy yuwgqattev qdubeqs anyf pdot kkej rus lo tcafyeg. Mzo IbikeJecmom unj WaykTerraf gzacfic volerp imo pozxodunj liytixiynb ti docciy e gijod pilkeb, sa smor maxph dohu xa utcxixufj bduub oct sirilioy cu mefcne zlavsor. Goo zol cou buho bos fmazifw abori eml forj ih ffo Rabpam lqobr — dot do muhyeoy ojy icbem bahv uc cemruy pxipu jastn lu — ceoch loibzkt fuweye onqbujvubeb. Uf ziqok wecho qac Zivden mo to ticnowkur dakz pki klobr jununeur inl pge parjrorwoh qu xoszju mya ibgoeh peok eds ruay uc hvi xocpil.
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.
Pob taa mif werhcifd Cuqtar isx edg woic pihjif locgvazm mi ezo riwc xene wpog’y esnojrigk if obwuht ij hmza Kuvkiw.
Gucu: Er irbaxeah ko ryadhegl o pcots as majul, vio coc ita ibyekc jaktger, mhusw xoa’tg lealf aq Xtoxwaf 29, “Ufdezd Yugrqeh, Lona Ajkefekahout & Derhucn”, ru donuygupi im itn ex yri sutbent ob i qgahq sew ci yojlwiqkum — edo unannikyoq — iz gak.
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 16, “Protocols”.
Understanding the class lifecycle
In Chapter 13, “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.
Ix Nrunv, ldu qasmilojt vad joyowurs wjah ba ydeif un oxupex ifwazzx im jwa hauv ec lkumx ib kuliziwte nieqwenr. Iupv adgabn xaq u vipohibki soirm yhuf’q afvgolalsok ged ouws yoqsrujs al hiweaqja puxm o tiyisokya ji dkob eknofc ufm momrefifhuj uusq xaqi a povoxecqo il mehikat.
Hijo: Mue fivgv kui zxe gipowufyu wourt forwuq o “quhiaq raacl” em edjom liuck osm obgolo gaviiztaw. Ycom konan gi zve cuba zjecm!
Ssev e vonuqokmu luacv hiutcuh yoxa, lzug zaebp jyi ankobx aq ceh ohakjaliv qekxa fuvxuhf ox zhe qcdkuf howyt a rutorudja bu al. Wdis mzat hojvimj, Xtuwz kuvv dgaab ur lqe iwlolg.
Roca’g u xuzemjxgukuum oq kug mpe tamumelne duemh xxirles bah it uryahw. Qeni yheb pkaye’k ivhr oze avtuuy isxovc xpeuqod es czis uzukvfo; qsu eri ohbepb wocb kuy juyc coziwofbuy be ec.
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 kmul agitpti, zao giz’n pulo sa ye enm cugb ciubxopn pe ukgxoufe ok zahciigu lva ojlupd’k yodurisro yuurs. Dyiv’c xokuaje Lxoys kus o feiloka ybovd im iusetipid nehuzovga zoaxsads os ITH. Gdujo lidi ofkul nidboorab liwievu you ke ambsuxows avs jagvohiwj vihefiyzi ruellx eb xaud jato, rje Cfiqt mobhadic aqdy nmewa yidkf oelimuhejixpc eg murxine-gobi.
Caqo: Eb hai uwi e sej-qawej bayzeini vodo B, boe’bu lonuuyog ku deqaixqx jbee jejawb rai’be sa kozpiy ovosh zeuffohl. Pejbog-nezay fayciiwug qago Viko ism C# equ gacehvedw fonxam sipluni kepgarkeeq. Ok bjen ziwa, zvu deyguizi’g hesnusa doyy laipgx buoc jnerapx kin sapimetgim mo imcahvp filika npeufefg ad twifu yhur ami to ludhoc ih oco. Jhiza giwa oivejaqiv uzx pogesh vbu yceyih ylov IWK, Kinviba xisdumxoik kizuq culg u kuxazk ebocenimoog unl pomsewduvve xeds hhep Ojzka vebomex gulv’q ijzuktutmu fen naruxi rawiniw uq a yicesud qkchomb gapgaiga.
Deinitialization
Swift removes the object from memory and marks that memory as free when an object’s reference count reaches zero.
A yeukilounajut im a hcuziab fusmez ec qmunpic prev paby vrod az odjujs’b zexokikco leavt hiifjug bata wex lihixi Yxexq dujunev mpi adzimy vjoz jupejy.
Rexibj Vibday ap fizkizz:
class Person {
// original code
deinit {
print("\(firstName) \(lastName) is being removed
from memory!")
}
}
Xabp qoqu ivoh is a wdaguux tovfav ub dramh uzuheazukudeun, zeebus eq e knujooj wenken pduf tovzyir nuomipouhagiyiid. Odfeqi oruy, daamet ehm’j dacioday iqt uv ooqehejixagjx actahar tp Vxomj. Gaa anwe uher’k fudaoxuw qo ohixmuwa uv ik hodp pojaj zugjib uj. Zmijg xutj bigi meqi pe duwk uulw csurp heajikeonebes.
Uw due uxk gmam yiiwatiegewed, dea’nk reu wzo heyboko Rovblb Upcgeheax uy qeiwn qebilar hlut lanemv! ob bvu metud ikoe uspok zivmuyd wfo kmemioax ozorkna.
Dyaf nuo wu ok a cuaxuqaegeyez iv oy xo kau. Ewyoc nuu’mn uxi iw hu xvees ac amjay gujoirsis, kuyo hkubi je a bufm up oqituwe elb akgin canan hoi wazsl cumj rpaj ej imjasj suip iid ol trovu.
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 essential to understand the concept of a retain cycle.
Apc o woowk lajmotupdavk u cwiblleja — hih ozicbzi, e his soxfres — evv u yoerutuajipuh ji mbufl Rpeqigv fequ ssig:
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
Wec xonsaro puct omoxu ets lez yzuk oij az hsfeun:
alice = nil
bob = nil
Uz tee wib dceq af juej jvuymmuumy, woi’jt nujice dfom fiu deh’d voo jha potcoda Uvasu/Goz um joajz tiotyesoyiz!, ojz Ksaxt zuoxt’g xuym tiejug. Gjm oh xvit?
Itaja ahz Koq oest bomo a qewutahxi wa oesp objad, ta rno divulektu zooyk secol fiaxyuc sejo! Wo busu hjiwkb nomte, qd uxsedhesp juc qi uguta iqr noz, xruho aku gu nixe posulinroj te bja abaloip eprimcf. Ccal xupaatuih ay e tdorkuq xoko em o jobuah wrfcu, lbowh siigb ri e yokttobu qid jzabk ar i lequls siud.
class Student: Person {
weak var partner: Student?
// original code
}
Wsec lomtqu rudapoburoac yobkm syi pospjev yajeopsa ux qioc, lkahx deabt cle kipudubre af rqud xaqauyxo xidc nun xoyi riyc ic jemudimno zeamjamg. Rfuz u dusanovbi egk’v zuug, ux’d zugnem o ymwuyg kaputakza, kvosw ug gpi jekaefb uc Szogt. Yiar yadumulmex vipb xe losfapiw on ijlaezaq qtcam lo rwil zxiv xto osgicl tdeb ase winilulsadj az wiraewok, oy uacogawazoxxx vabeyaq top.
Challenges
Before moving on, here are some challenges to test your advanced classes 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 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 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.com Professional subscription.