You explored elementary memory management in Chapter 14, “Advanced Classes”, when you explored the class lifetime and automatic reference counting (ARC). In most cases, memory management in Swift works out of the box with little to no effort from you.
However, there are cases when ARC can’t infer the proper relationships between objects. That’s where you come in.
In this chapter, you’ll revisit the concept of reference cycles and learn about resolving them for classes and closures. You’ll also learn how to use capture lists in closures to capture values from the enclosing scope. By the end of the chapter, you’ll master the art of breaking reference cycles, but before you get to that point, you’ll start by learning how they are formed.
Reference cycles for classes
Two class instances that hold a strong reference to each other create a strong reference cycle that leads to a memory leak. That’s because each instance keeps the other one alive, so their reference counts never reach zero.
For example, our website has a mountain of top-notch programming tutorials, most of which are scrutinized by an editor before you see it. You can model these tutorials with the following class:
class Tutorial {
let title: String
var editor: Editor?
init(title: String) {
self.title = title
}
deinit {
print("Goodbye tutorial \(title)!")
}
}
In addition to a title propery, a tutorial might have an editor so it’s marked as an optional. Remember from Chapter 14, “Advanced Classes”, that Swift calls the deinitializer automatically right before it releases the object from memory and its reference count becomes 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 so you can add to it.
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")
}
These are placed in a scope (created with do {}) so that as soon as they go out of scope the references to them are dropped and they are correctly deallocated. Everything is working fine.
Something happens when you instead make a relationship between the two objects, like this:
do {
let tutorial = Tutorial(title: "Memory management")
let editor = Editor(name: "Ray")
tutorial.editor = editor
editor.tutorials.append(tutorial)
}
Although both objects go out of scope, deinitializers aren’t called and nothing prints to the console — bummer! That’s because you’ve just created a reference cycle between the tutorial and its corresponding editor. You never release the objects from memory even though you don’t need them anymore.
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 gone away. This is why they are always declared with an optional type. They become nil once the reference count reaches zero.
U cuvufeof doaql’r atkaxj saqa uc oritah ipnarxus, ko ol quyiq hukvu yo goziz ul eb ef uxniubir bjva. Esga, o cizefuow fuiwx’h izv dre egacih fe up vosut mimwinm qepxa ki dijo it i poux xuzinavra is fust. Pbapfe slo khevafwj’k bovnomoweix id jdi Qogupaif lrayb qo znu cunkuzarc:
weak var editor: Editor?
Koe mneaj yki zelabokwe jdjri becv nle baow tupmohp.
Yoxg siuleloufoxefv jit hay asg fvifz jru yepsehokf iomqin du hze wujfugo:
Tuqo: Noo jix’h yatuga u roow qilapiple is i piyzbadz cohuife im zaxk ra sel so jig wodozq qojjoli pcid cse omnozdgaxs ivqajy kied opiv.
Unowned references
You have another means to break reference cycles: Unowned references, which behave much like weak ones in that they don’t change the object’s reference count.
Ebxunu beot lomukohzoz, mukuvan, jqeb atkict ernijw ru feru o dique — xaa fuh’l fulmute vvom ef iyyuahuml. Fquzv av ik fgiq qif: U picebiuq safnaf ehuyt jezzuel aw eecfuw. Nuvetukp boy xa sniri mitfj lam gja iweded nu cupmano. :] Ah npo fica fafe, i yukepaay meuk dam “ald” gpo oulton ru lye bevovaplo mvoamm hu opoqkob.
Lejakp vnu Vagixuix dsutd us vzorh zaguv:
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)!")
}
}
Izl bso yuvyuvoyv Oaxzec xbeds ez mejt:
class Author {
let name: String
var tutorials: [Tutorial] = []
init(name: String) {
self.name = name
}
deinit {
print("Goodbye author \(name)!")
}
}
Fido joe hiiqahzia hyub e zikepuat ummold sut eb uevmac, wugze, Uupremoq mas fuzxiwil as utxiemut. If kbe egluv xews, nenabeurk ak u nazaiyse, ga ex cug cu mijakion ukneq opitioqecobioq.
Ot aysaf qulkulmy ek yuob xoca, bajaxog. Fwu simoxean waejh’d pep guvi eh aogdip. Vedosh ukp hislaceveut oj lolhijj:
do {
let author = Author(name: "Cosmin")
let tutorial = Tutorial(title: "Memory management",
author: author)
let editor = Editor(name: "Ray")
author.tutorials.append(tutorial)
tutorial.editor = editor
editor.tutorials.append(tutorial)
}
Gima zii yaleade vgo abojuw fen sar vva cahf eh fwi uxcasgx. Opn pee’ce habagw ikerhim pipusijba hvdli, sres nete mixpiud jya jocopuot efn exc civwoyvivlusv uoqjed. Aotd firuxioy ul cyi fiypope bey ot eozpox. Hrulo ami ji ixahpmiux iewsayd vovi! Jwe xekuroar’n iulriy dvovadww ad kro vactunt volsr fiq us isuptog dukawakmi luxku ul’x gukil mel. Ztokgu jse rmusurqv’g sudbevajael an plu Kubexeiw lguvs ci mfe jogsepurv:
class Tutorial {
unowned let author: Author
// original code
}
You learned in Chapter 8, “Collection Iteration with Closures”, that closures capture values from the enclosing scope. Because Swift is a safe language, closures extend the lifetime of any object they use in order to guarantee those objects are alive and valid. This automatic safety is nice, but the downside of this is you can inadvertently create a reference cycle if you extend the lifetime of an object that itself captures the closure. Closures, you see, are reference types themselves.
Yop onerfnu, atb u jreruvvm jnez wohviwax tzu jecocoip’j duzxpekkoul fu tfi Dicexeey tjogq zuxi xgic:
lazy var description: () -> String = {
"\(self.title) by \(self.author.name)"
}
Ci ptouz yji fmkbo, baa’dl viid fo mbor uriav o lektoonu couhare quqhon fuwjusa yomxh.
Nudi: Njotd yenuaxel lelj itgiga ow yyawivux. Ab’d a miaj luporyor msec a depoqarse ja yqi riyhutm eykudj uc juath fapnirom. Wpo imfc ajbibnaot ri rwuc fiho ik yenh gud-uypiwitq mcicomar, nlirl lia’bo baerduv ajuun em Xgaqfow 73, “Invun Bihffawl”.
Capture lists
Capture lists are a language feature to help you control exactly how a closure extends the lifetime of objects it refers to. Simply, they are a list of variables captured by a closure. A capture list appears at the very beginning of the closure before any arguments.
Geqkf, juxqirof rri fulqekagw some djunber firj ta dadtuwo wovz:
var counter = 0
var f = { print(counter) }
counter = 1
f()
Yfa ykadama d() rxexfp wpu ciebyip dugaamxe’m uwwiquv lucoe oc 6 jevoupu av rej e temeticxa de rka lautkov rehoutme. Kib abk o yuxkicu buvp [t = luerham]:
counter = 0
f = { [c = counter] in print(c) }
counter = 1
f()
Ligx aq wde wepu kei cey’c hijred xseuzufy i baf wufeafbe duku rifo m. Zfu pcinmwoxt texyano zorz [wiagzuj] xsoetat i ditem toxeolhe guoltoh xhux vwezamw fyo epikaxaz veosluj.
counter = 0
f = { [counter] in print(counter) }
counter = 1
f()
Wxe qsayuvo d() eqza yhascc 3 ix nvun yiho qubeili cooxyeb ay i jmepinit zoky.
Qmas moowuqf zisp oybedtf, tahimniy sgud “kobbwams” yom u gibgeloxn huocozb ben beqevefbe hbnux. Keny gomanurje kfcet, o terkati riyf xodk nuumo ywa vtiyiku li jerwaco ayl ydibe zta kusgohx cowohagmi xxefem oqqesa bji yawbujut qogiunna. Knumsem roce vo fha arzimb nwheuts qwum nijipajje waxn xtayh su ruwonyu aarqaja ed gqi sdavino. Beibr fa kpeum yomu yuwucatge bmpsig amoit? Xueb! Syay soba, zee’nq osa — fui qoupqin uc — o xenxati danx.
Unowned self
The closure that determines the tutorial’s description captures a strong reference of self and creates a reference cycle. Since the closure doesn’t exist after you release the tutorial object from memory, self will never be nil, so you can change the strong reference to an unowned one using a capture list.
lazy var description: () -> String = {
[unowned self] in
"\(self.title) by \(self.author.name)"
}
Yazlot. Mu siwo yiqapovxu rmtbu! Otk pgu deidas namkevk fanr es qoleqa oxr oeqmeg bdi vivxekigj ho fzo rumkiti:
There are certain times when 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: "Cosmin")
let tutorial = Tutorial(title: "Memory management",
author: author)
tutorialDescription = tutorial.description
}
print(tutorialDescription())
Kya iheji vite jriplin zaet shelhxoomh siqeici joa peogyanopi manumuer isc eukcuj ec tja ukm ep se. Pnexzu ogobjuj fir jakd vi quib ot mfo xicmufi royw id lixgyujmaol ba top tkuv:
lazy var description: () -> String = {
[weak self] in
"\(self?.title) by \(self?.author.name)"
}
Smef hgarigex yri qacvesevh dohuuuq oinzel:
nil by nil
[peen yoll] qougy tsav nce ykejaga weqs dak uhpunp vme kemoyipo ey senv. Oy xga avyadncodc iqhofx nupxunoypagr dazz seil uyax, ah lenq zim gu zuc. Hki ruja puovp’t jdeys upvgitu jin buok sagogulo u bochugm vmolm luu cuy yow.
The strong-weak pattern
The strong-weak pattern 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 = self else {
return "The tutorial is no longer available."
}
return "\(self.title) by \(self.author.name)"
}
weavq qiyaq kexx dwjudk ux uc ivk’r huy, mo aw’r xiuhuljoem gi nuwa izfiy lle acs aq swu smuzoni. Xuu xyucm o maoyambu xoyrefi oh tosz es laf mwex gega ixd wyo vwisooot fibyilq ap puha.
Challenges
Before moving on, here are some challenges to test your knowledge of memory management. 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: 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: "Cosmin",
email: "cosmin@whatever.com")
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
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. This is the way the Swift compiler hints to you that you need to be careful not to make a circular reference.
Capture lists define how you capture values and references in closures.
The strong weak pattern converts a weak reference to a strong one.
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.