1

Now when a closure refers to self within its body the closure captures self, which means that it holds a strong reference back to the HTMLElement instance(see below). A strong reference cycle is created between the two. This means that the instance won't deinitialize (as shown below); however when I tried to replace self with heading (the instance) deinitilization did work which means that a strong reference cycle didn't exist. My understanding is that heading in this case is the same as self, since self is the instance itself. So why did the instance deinitialize when I replaced it with the instance 'heading'?

class HTMLElement {

let name: String
let text: String?

lazy var asHTML: () -> String = {
    let defaultText = "some default text"
    return "<\(heading.name)>\(heading.text ?? defaultText)</\(heading.name)>"
}
init(name: String, text: String? = nil) {
    self.name = name
    self.text = text
}

deinit {
    print("\(name) is being deinitialized")
}

}
var heading = HTMLElement(name: "h1")
let defaultText = "some default text"

print(heading.asHTML())
// Prints "<h1>some default text</h1>”
heading = HTMLElement(name: "h4")

output:

< h1 > some default text < /h1 >
h1 is being deinitialized

Now if the closure was replaced as follows:

lazy var asHTML: () -> String = {
let defaultText = "some default text"
return "<\(self.name)>\(self.text ?? defaultText)</\(self.name)>"
}

the output would be:

< h1 > some default text < /h1 >

rmaddy
  • 314,917
  • 42
  • 532
  • 579
User9123
  • 73
  • 6
  • Doesn't seem duplicate to me. It's not about lazy closure vs lazy String variable. – algrid Sep 06 '17 at 17:34
  • This code demonstrates how ARC works: when you're referring to `self` in a closure ARC retains the object for you to prevent it from deallocating so you won't get into trouble when calling that closure. But when you're referring to a global variable there's no point in additionally retaining any object - that variable will always be available for you. – algrid Sep 06 '17 at 23:06

1 Answers1

1

The correct way to avoid strong reference cycle with closure is to mark self as either unowned or weak, e.g.

class HTMLElement {

    let name: String
    let text: String?

    lazy var asHTML: () -> String = { [unowned self] in
        let defaultText = "some default text"
        return "<\(self.name)>\(self.text ?? defaultText)</\(self.name)>"
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) is being deinitialized")
    }

}

It begs the question why you don't just use a function

func asHTML() -> String {
    let defaultText = "some default text"
    return "<\(name)>\(text ?? defaultText)</\(name)>"
}

or computed property:

var asHTML: String {
    let defaultText = "some default text"
    return "<\(name)>\(text ?? defaultText)</\(name)>"
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044