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
someMethodaccessessomeConstantviaself, why doesn’t it cause a memory leak? - if
someMethoddoesn’t cause a memory leak, why doessomeClosurecreate a memory leak when it’s equal tosomeMethod? - if
someClosurecreates a memory leak andotherMethodaccesses and returnssomeClosure, why doesn’totherMethodcreate 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