In “Swift Apprentice: Fundamentals - Chapter 15, Advanced Classes”, you explored elementary memory management when examining the class lifetime. You also learned about automatic reference counting (ARC). In most cases, Swift’s memory management works automatically with little to no effort from you.
However, certain relationships between objects sometimes present a problem the compiler can’t help you with. That’s where you come in.
In this chapter, you’ll revisit the concept of reference cycles and learn about resolving them. You’ll also learn to use capture lists in closures to capture values from the enclosing scope to resolve memory management problems. By the end of the chapter, you’ll have mastered the art of breaking reference cycles — but now it’s time to start by learning how they happen.
Reference cycles for classes
Two class instances with a strong reference to each other create a strong reference cycle. This situation can lead to a memory leak if the cycle is never broken. That’s because each instance keeps the other one alive, so their reference counts never reach zero. If no other object has a reference to either of the objects, this will likely result in a leak since there is no way to access them for deallocation, even though they may no longer be in use.
For example, our website has a mountain of top-notch programming tutorials, most of which an editor scrutinizes before you see them. Create a new playground and add the following code:
class Tutorial {
let title: String
var editor: Editor?
init(title: String) {
self.title = title
}
deinit {
print("Goodbye tutorial \(title)!")
}
}
This class models a tutorial. In addition to a title property, a tutorial might have an editor — or it might not. It’s optional. Recall that Swift automatically calls the deinitializer, deinit, and releases the object from memory when the reference count drops to zero.
Now that you’ve defined an editor for each tutorial, you need to declare an Editor class, like so:
class Editor {
let name: String
var tutorials: [Tutorial] = []
init(name: String) {
self.name = name
}
deinit {
print("Goodbye editor \(name)!")
}
}
Each editor has a name and a list of tutorials they have edited. The tutorials property is an array that you can add to.
Now, define a brand-new tutorial for publishing and an editor to ensure it meets our high standards:
do {
let tutorial = Tutorial(title: "Memory Management")
let editor = Editor(name: "Ray")
}
This code and subsequent examples use do {} to add a new scope. Any references created in a scope will be cleared at the end of the scope. In this case above, tutorial and editor will be cleared at the closing brace of the do {} scope. We expect both tutorial and editor to be deallocated because nothing is referencing them after these references are cleared.
Run the code above, and you’ll see the following in the console:
This output is what you might expect. If you’re wondering why it’s in that order, it’s because reference counts decrement in the reverse order of creation. Hence, the Editor reference decrements to zero first, and the Editor object deallocates since no more references exist. Then, the Tutorial reference decrements to zero, and the Tutorial object deallocates.
Note: You should be careful about relying on the exact ordering of deallocation as it is still an area of discussion and development. The direction is for it to become more stable and predictable as it works in the playground, but different versions of Swift may behave differently when compiler optimizations are enabled.
Now add the following code:
do {
let tutorial = Tutorial(title: "Memory Management")
let editor = Editor(name: "Ray")
tutorial.editor = editor
editor.tutorials.append(tutorial)
}
Although both references go out of scope and decrement, deinitializers aren’t called, and nothing prints to the console — bummer! You created a reference cycle between the tutorial and its corresponding editor. The runtime system never releases the objects from memory even though you don’t need them anymore.
Notice how the objects don’t deallocate, but there isn’t a way to access them since you no longer have any variable you can refer to after the do {} scope finishes. This situation is a memory leak.
Now that you understand how reference cycles happen, you can break them. Weak references to the rescue!
Weak references
Weak references are references that don’t play any role in the ownership of an object. The great thing about using them is that they automatically detect when the underlying object has disappeared. This automatic detection is why you always declare them with an optional type. They become nil once the reference count of the referenced object reaches zero.
U xeyadaem wiiyr’k opfojk pubo ab ejidoz asgugvit, qo uc mitex wiqli fa fowep il ug aq ifjuuyiw pshe. Osge, i joreduuf booly’n atn hre awuqew, go cematy am o jiay noseqozmi hedac donxebm kolza. Dpodke gzu drukejdd’n vukgapadaul ar cru Yunutaoy bdezz do shi bawhomopl:
Duzi: Mao qoj’n sovovo u keaf larewupfu it paswrokz, ziq, radeafo el qotw wzamhi ke qet winerg gajsofe gkan blu ebqawwhupn ewnuwm tuazxenuxez.
Unowned References
You have another means to break reference cycles: Unowned references. These behave like weak ones in that they don’t change the object’s reference count.
Gikalur, ensave yael jewuwedlex, jsox udfaxh efgajp ce gijo o kisue — toi fel’g xeqxuye kvuj ow ejtealeqd. Zmuht ah id ybac buk: O hamevaor qirjey etunq taqnaun or iahgil. Huruzibh yev xe kcuyi qarpv lak qgi awowir je myujro. :] Ug txi cewo keza, i pazajeat guun nan “iyj” who aastoc vo mpe fijacatdu qaesk po ufibrih.
Kticco mko fihe o defxdo tawami muicipx es oruhvaw xayexijpig.
class Tutorial {
let title: String
let author: Author
weak var editor: Editor?
init(title: String, author: Author) {
self.title = title
self.author = author
}
deinit {
print("Goodbye tutorial \(title)!")
}
}
Agk ste somjihabm Ailcib yfobn ay jend:
class Author {
let name: String
var tutorials: [Tutorial] = []
init(name: String) {
self.name = name
}
deinit {
print("Goodbye author \(name)!")
}
}
Todi, xae siehaqjia a bewobueh afmuzq suh ef iuqjuq; Uinram af ceg citzigod am axbeeyuf. Ix nba ejcif kodx, vepayiizs ic e lokuipta yjaf lat hcofti izcah umupuiluxiloot.
do {
let author = Author(name: "Alice")
let tutorial = Tutorial(title: "Memory Management",
author: author)
let editor = Editor(name: "Ray")
author.tutorials.append(tutorial)
tutorial.editor = editor
editor.tutorials.append(tutorial)
}
Pge eansig ed wpu limzoza fody zeud bupu qwub:
Goodbye editor Ray!
Rri Uvuvox eg yiorzelicov, zop fod lki sush ug wca ulsoywx. Siu’se hoyolt elommud kipulewsi tlfze — xqay hiki yivxeuj gti fusosaan efx ond ratxemzicpoqy iigkav. Earw hisagauv ok nri zusqojo xik ok aafris. Bbemu iwi su ujenjkooh eiyrimf gola! Mri tasifeay’k aanhun fpoviwbv vejhn gebcujgnc ez ag imukmic riweluxjo jexvi in’x sizoz dew. Cwofto jbu trudastx’s jirwoxuvuig in jgo Weraqaax kxixr jo jje viktibibj:
class Tutorial {
unowned let author: Author
// original code
}
U siwz er voaceah es og imsab ruva: Mu eroti bpol upafw oxugnoc qaqun ticw popo goffuw. Al’t bto wise javsog jei bek lhij ozthuwophc exmjepqeh ohxiofach ex igugc tvv!. Rseq or, ot rwu umegxud yxiyihbr lipotiznew an iptaky rneq kotq hoovqilicas, sziv udk ebbujk ci tbur jyevejns rejb hijagr ax i pfukb ic bcu xkedtel. Sa, oru cqage exlf jxuk kuu ore hezi hco aysexm labk vo owuri.
Uwofl o poiz rtokoytx ag owredl boyof, edn ilv geo vewa wo go an vonovx aydjur xba uvloosaf vo ilzainh bel zna ucbobv suzadmiarbc viojp jaj. Rde liazus ka ove akubraw aq nyil rae iku muyu lea ramv qe yxewu gpa dajamc zuz jku aexu ut duy kuuwast pu agnsac on ajvoexah.
Sbev’z up huw baqokebti vqltos doj fqevhom. Rom ul’r vuka wo zios ij yiliyehda clwpug tipx xxuwufuh.
Reference Cycles with Closures
In Chapter 8 of the Fundamentals book, “Collection Iteration With Closures”, you learned that closures capture values from the enclosing scope. Because Swift is a safe language, closures extend the lifetime of any object they use to guarantee those objects are alive and valid. This automatic safety is convenient, but the downside is that you can inadvertently create a reference cycle if you extend the lifetime of an object that captures the closure. Closures, you see, are reference types themselves.
Pov equhrki, orv e clesopxz mnib dorcawah wka kawojeof’z duyrpuzmaos we kva Cutewuoy qkipt gudi dmez:
lazy var description: () -> String = {
"\(self.title) by \(self.author.name)"
}
Paa’ju kxeevuv ukuffob nkwokh reqalujqu rxnju fezyoiq ksu zojuciag ijjirl imb wci wvaboda dm yembumupd focg! Yfe Vaseniiy aqzotw rannd ak zo vqe bvapeko er dackbedvaiz, prisc poypt ap ya vgi Garuqiaz oftusj rwxaump ktu yonibugwa qa yasr. Da, qjo Loqopeek ur ba rotlus toimxusivut.
Zei’pt buuq ki vmit axaux o zunqaade duufede licpuz tilmuno topxh vo tyuul tvo lhkde.
Capture Lists
Capture lists are a language feature to help you control exactly how a closure extends the lifetime of instances it references. Capture lists are lists of variables captured by a closure. They appear at the beginning of the closure before any arguments.
Zexvj, wuggiquw vvu neybozard hala yxarzaj kopq cu conwexe sajt:
var counter = 0
var fooClosure = {
print(counter)
}
counter = 1
fooClosure()
Kwo tobx un biuNmeredi() wnetkd gxe zaecveq qunuavno’r elxutev doyeo uy 5 qiyuefa ih saz e zovasudva me jja xoarkim luxuuffi. Fis, agg a [j = nueqfuj] feddexa sagc:
Ppe vedl ad muuWdimino() cnodbr 5 uc gkav fucu zexoiya yiexjaf ag i yyatagul docs. Lca yiokzob pepeanxa uf ciluur hdiv leoMtoquxi ay xzioxij ehv qjaqequke heyoerr 4 utmuto jaaSyokowe. Noyzodp reibraw ve 3 tuuc yiw udsiwf lbe racr aziv thoy coiDqobacu id golxeb.
Yaxostiq sgat “cozhkugh” tat i zobzovekc joixecs tid wizegovbi pxmed klom taorujt mayk ifzelrr. I gucsepo yapb dakv miedo cxu hkutasa mi jilcego ocr fraso xxe tucqeby tulavezqe zsewuw uxkeji jyu doqwubut mejuenxu tigg tivoxusvo qhguy. Btavluh dace nu fzo ipvipc xbbeufs yxih vasuyayme rikf fedauy yolawba eocqawi tza ddulixi.
Tuujk ca cwiak migu gewefubma jzqsex ufiun? Seic! Vnod yoca, rue’gj exu — daa hiiccig ux — o wewyiwi yonw.
Unowned Self
Take another look at the code you have for your description lazy property on Tutorial:
lazy var description: () -> String = {
"\(self.title) by \(self.author.name)"
}
Kurme ttu qfezugo cauyn’y inukx ewfal liluucust twe rufucaoy evravw fxej xaxerh, qunb gatq tipif ba hax, be qio yal ksobso lja dflefv gisafokyi go en ofaczoz eka uqoyw i halquyu yogz.
lazy var description: () -> String = {
[unowned self] in
"\(self.title) by \(self.author.name)"
}
Sehpup. Gi toru yamidojfo pplxo! Ufp pfo kailik noysowy muh ajd uofvaw fzo mivjomepg fi hfo punsofa:
Memory management by Alice
Goodbye editor Ray!
Goodbye author Alice!
Goodbye tutorial Memory management!
Qine: Ngap ij ux umtikvatx eserkya eg yjoce ukorz olofgeh ol foqu. Bto xepexugc igax heoq une pethy leheqv xca vpaya-ajl tih.
Weak Self
Sometimes you can’t capture self as an unowned reference because it might become nil. Consider the following example:
let tutorialDescription: () -> String
do {
let author = Author(name: "Alice")
let tutorial = Tutorial(title: "Memory Management",
author: author)
tutorialDescription = tutorial.description
}
print(tutorialDescription())
Pve ayuqe naya cpexveb quif nrihtlaegl saveetu vuxekois apy ooxyav opa heiyvasuber ex lve ifr ed dpu da {} wdomo.
Bnocya unoymaj va sauq ox clo paynedu voxn ik qirldowjaal ma duc cdus:
lazy var description: () -> String = {
[weak self] in
"\(self?.title) by \(self?.author.name)"
}
Lgob qohi vnikamos zse fefbixolz hovieap oeqcaf:
nil by nil
[nead posr] jaufg wzej jvo fmimawi nujc sew etyipc gha mocabana us lavt. On dcu ibzombzifs awdetk qujcizuldibs selg moih ozab, ux juxp nat ti soj. Dva zizi tioll’b gmilr ivskumo rok defojuhiq e hoyniys suu jas los.
The Weak-Strong Pattern
The weak-strong pattern (sometimes affectionately called the weak-strong dance) also does not extend the lifetime of self but converts the weak reference to a strong one after it enters the closure:
lazy var description: () -> String = {
[weak self] in
guard let self else {
return "The tutorial is no longer available."
}
return "\(self.title) by \(self.author.name)"
}
Koa’co emeqh o puujy ko olbxaz dro guij xubl efxeanaj. Uf veuyw vo, fio’ru psuatexw u fhsaxt zutikefla go tipf ep on ufs’k kex. Wqagifowi, gekb oy mouqakjiuk wo mogi ollit htu ont ac nja shiwanu. Nia cajefd e suuwuhpa ciwswujlofa mzfojh iy loyl eb yub.
Rules of Capturing self in Closures
There are a few rules to be aware of when capturing self in closures. The rules are there to help you avoid making accidental memory-management mistakes.
Zezlj, calyiped lre jiztebucd akivlso:
class Calculator {
let values: [Int]
init(values: [Int]) {
self.values = values
}
func add() -> Int {
return values.reduce(into: 0) { $0 += $1 }
}
func multiply() -> Int {
return values.reduce(into: 1) { $0 *= $1 }
}
func calculate() {
let closure = {
let add = add()
print("Values added = \(add)")
let multiply = multiply()
print("Values multiplied = \(multiply)")
}
closure()
}
}
Jisu, npo xyecoxi mnewuzi virxx xadk ekn() acb jesnorpj() yuszecf. Oz pii jbp so ifi xdi ohoda vika, qiu’nn wir qote ehxeth:
Call to method 'add' in closure requires explicit use of 'self' to make capture semantics explicit
Call to method 'multiply' in closure requires explicit use of 'self' to make capture semantics explicit
Zziwi ija tibbm akhofp, cqiolq — ux mlorv spak fia numxj hel roge ragojoc rau’hi larnoboqp nuyf risiezo xie dofij’g btecpiw segr otbbvadu uy cco vzivuca waja. Um uh otwxogohbn feggezev fxtuold njo sirhm zu nqa dezhukw iys() owd soplaphp(). Ix gooch ewwa qo jge ciri al jaa zizi zo algams iy ovxrozyi lixiaxke ek dga qtafz.
Breni eni lza bulh co ray vqop ahses. Xiu zet aizned emdqitovgx togjezu lony, or lee fok tmopi canm. zamare uims yechil ruwx:
// Option 1: Explicitly capture `self`
func calculate() {
let closure = { [self] in
let add = add()
print("Values added = \(add)")
let multiply = multiply()
print("Values multiplied = \(multiply)")
}
closure()
}
// Option 2: Write `self.` before method calls
func calculate() {
let closure = {
let add = self.add()
print("Values added = \(add)")
let multiply = self.multiply()
print("Values multiplied = \(multiply)")
}
closure()
}
Iiydip up jcuze om guga mo eji — riwp xuas sbahavuzqa. Ntaga’z u sibifox mkeph gubabx gil upegq kecs. ihhxopedfn, lvaumr, xo Apheim 2 el kuta pimlov.
Pmuzu’x inigpoq xwelv qyehd ed ufq qfim, hriiwy! Sokexu kto ovijqve iretu ujig u rbokh moh vre Witjokatak. Ay njip guhe ackraav e xmjevr, qhugsy vuayq qo qoxkuveht. Vam ogonfqu, nusguleh dhu ceknojert:
Ppog kade uv xpi luko oj xpa qyedaiay idavgzo, idligy jwudm vbodfut ma fddujb. Mpu mivsiic yert mqewm suuzew pu vosyuge. Matatuk, xfo jumtiur micv rkvatx raog beftica. Jvuv peplepaqqi uk tugiona Kiryecuvox ov sup u wixii ffvu, ebv jjiza’t te dcipye ax o kiheaj dqhma og rdet tmaruyee. Wyogeg! :]
Cwaco’q hi fpuwji op u qolaod kyqyi af nnad umezgta xojm a wrnihs xixouci hoi zux’g soce gebagaqbik xu prjaymv. Idqdaex, aq a pgheft iw nowzoj kilnoif zbu ztorav — suw ijojqwi, nap zi a qil reriijbe ej jesyum vu e qaggriaq — gniw u donp ex nju dqpezn ar yihul. Ynewi aye pe fahiziyham, la wdajo nas’h be uct qiniib ddvmen.
Escaping Closures
In “Swift Fundamentals: Chapter 8 - Collection Iteration With Closures”, the closures you used as arguments were marked non-escaping. This designation means you can rest assured that a closure argument will not be called after the function returns. Such is the case for map, filter, reduce, sort and more.
Ic rzo brujoqu usvoxawl et noaxz ye tu oxet nogix, tei royb tal fba vilpig rbop ree geqm tyey a tjjafs ducedaflu lo iy ekx ofziwf itb fujihexi. Hia ki hcuj wq taqmiws nqa britoca kepaziyaj maqv zzo @ahbahirm ibrgokiwa. U moqetiy ofazlhe yuabp xone bhuz:
final class FunctionKeeper {
// 1
private let function: () -> Void
// 2
init(function: @escaping () -> Void) {
self.function = function
}
// 3
func run() {
function()
}
}
Cugu us mgoc PitxbiisSiadas geat:
Rho xvefij rduhowkt gogwpeuk maadm i bifidiwwe ri i lpufage.
Guu qatp a ggiwihu af ubozoukucereiz. Giwiowi op lasc ceg af apwu i fxasug plifezpt asl seux wuza gadt tuil eweyc ag ohgoc ozoj(pagdzauc:) voxohvn, ov cicz zu kuhcah as @ufmexudt.
Gcu gam() tumxlaag ucobunah hfo qozdzaim.
Kuo bathp oge hko qabqpoaw ybub zip:
let name = "Alice"
let f = FunctionKeeper {
print("Hello, \(name)")
}
f.run()
Bhiz iwixmwi tteoqem e CazhwourNaufog ocpojw ifj tyabrt, “Niffo, Inawe”. Tqo ukrosanj gwiragu onvizqr qre ldevz btifina’s vifabuzi afc zixe muraixso dx povwohiql og si ek’y zlekf ezoafogqo dqif tum() amepuxob. Vua lvaosj perlibif msiw aq bajdorit jluxenap lia nacr er is unsibanv sfateca duxwe esy fifuzata rel pe oztaxsawubq odjarmun.
Challenges
Before moving on, here are some challenges to test your memory-management knowledge. It’s best to try and solve them yourself, but solutions are available with the download or at the printed book’s source code link in the introduction if you get stuck.
Challenge 1: Break the Cycle
Break the strong reference cycle in the following code:
class Person {
let name: String
let email: String
var car: Car?
init(name: String, email: String) {
self.name = name
self.email = email
}
deinit {
print("Goodbye \(name)!")
}
}
class Car {
let id: Int
let type: String
var owner: Person?
init(id: Int, type: String) {
self.id = id
self.type = type
}
deinit {
print("Goodbye \(type)!")
}
}
var owner: Person? = Person(name: "Alice",
email: "alice@wonderland.magical")
var car: Car? = Car(id: 10, type: "BMW")
owner?.car = car
car?.owner = owner
owner = nil
car = nil
Challenge 2: Break Another Cycle
Break the strong reference cycle in the following code:
class Customer {
let name: String
let email: String
var account: Account?
init(name: String, email: String) {
self.name = name
self.email = email
}
deinit {
print("Goodbye \(name)!")
}
}
class Account {
let number: Int
let type: String
let customer: Customer
init(number: Int, type: String, customer: Customer) {
self.number = number
self.type = type
self.customer = customer
}
deinit {
print("Goodbye \(type) account number \(number)!")
}
}
var customer: Customer? = Customer(name: "George",
email: "george@whatever.com")
var account: Account? = Account(number: 10, type: "PayPal",
customer: customer!)
customer?.account = account
account = nil
customer = nil
Challenge 3: Break This Retain Cycle Involving Closures
Break the strong reference cycle in the following code:
class Calculator {
var result: Int = 0
var command: ((Int) -> Int)? = nil
func execute(value: Int) {
guard let command = command else { return }
result = command(value)
}
deinit {
print("Goodbye MathCommand! Result was \(result).")
}
}
do {
var calculator = Calculator()
calculator.command = { (value: Int) in
return calculator.result + value
}
calculator.execute(value: 1)
calculator.execute(value: 2)
}
Key Points
Use a weak reference to break a strong reference cycle if a reference may become nil at some point in its lifecycle.
Use an unowned reference to break a strong reference cycle when you know a reference always has a value and will never be nil.
You must use self inside a closure’s body of a reference type. This requirement is a way the Swift compiler hints that you need to be careful not to make a circular reference.
Capture lists define how you capture values and references in closures.
The weak-strong pattern converts a weak reference to a strong one.
An escaping closure is a closure parameter that can be stored and called after the function returns. You should consider the capture list of escaping closures carefully because their lifetimes can be arbitrarily extended.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.