I have a few questions about methods and closures in their relation to each other and memory leaks as I'm not sure why closures lead to memory leaks when methods don't. My assumption was that if a method doesn't create a reference cycle, then assigning that method to an instance property wouldn't lead to a reference cycle. Similarly, I assumed that any method would create a reference cycle if it accesses and returns a closure that creates a reference cycle. It turns out that neither of these are the case, as is demonstrated in the code I wrote, but I'm not sure why.
I’ve written three separate do
blocks to create three different scopes.
In the first do
block, I’ve created an instance of Car
and accessed its someClosure
property. The print statement in the init
method gets executed but the print statement in the deinit
method doesn’t, hence a memory leak occurs.
In the second do
block, I’ve created an instance of Car
and accessed its otherMethod
method. Both of the print statements in the init
and deinit
methods get executed, thus indicating no memory leaks.
The final do
block more or less exhibits the same thing as the first do
block.
I have three questions:
- If
someMethod
accessessomeConstant
viaself
, why doesn’t it cause a memory leak? - if
someMethod
doesn’t cause a memory leak, why doessomeClosure
create a memory leak when it’s equal tosomeMethod
? - if
someClosure
creates a memory leak andotherMethod
accesses and returnssomeClosure
, why doesn’totherMethod
create a memory leak?
class Car {
init() { print("Car is being initialised") }
let someConstant = 1
func someMethod() -> Int {
someConstant
}
lazy var someClosure = someMethod
func otherMethod() -> () -> Int {
someClosure
}
lazy var otherClosure = otherMethod
deinit { print("Car is being deinitialised") }
}
print("---------- block 1 ----------")
do {
let car = Car()
print(type(of: car.someClosure))
}
print("---------- block 2 ----------")
do {
let car = Car()
print(type(of: car.otherMethod))
}
print("---------- block 3 ----------")
do {
let car = Car()
print(type(of: car.otherClosure))
}
// The following gets printed:
// ---------- block 1 ----------
// Car is being initialised
// () -> Int
// ---------- block 2 ----------
// Car is being initialised
// () -> () -> Int
// Car is being deinitialised
// ---------- block 3 ----------
// Car is being initialised
// () -> () -> Int