WishIHadThreeGuns
WishIHadThreeGuns

Reputation: 1469

Retain cycle in a closure Swift

I can see many examples of retain cycles in Swift. However many of them are incorrect, and reading the documentation does not make really simple examples that I can follow.

For the example:

class Dog {
    func bark() {
        print ("YAP")
    }
}

var dog = Dog()
let doSomething = {
    dog.bark()
}

doSomething()

Does the closure doSomething cause a retain cycle? I understand that the closure will execute quickly, but that is not the question. Does this inherently cause a retain cycle?

Upvotes: 0

Views: 997

Answers (2)

rob mayoff
rob mayoff

Reputation: 385600

There is no retain cycle in the program you posted.

What is a retain cycle?

Consider each object (including each closure) in your program as one vertex in a directed graph. Consider a strong reference from object (or closure) A to object (or closure) B as an edge from A to B in the graph.

A retain cycle is a cycle in the graph: a path containing at least one edge (strong reference) that leads from a vertex back to itself.

For example, a typical retain cycle looks like this:

retain cycle from ViewController to View to Closure back to ViewController

A view controller always has a strong reference to its view (if the view has been loaded). In this example, the view controller created a closure. The closure captured (has a strong reference to) the view controller. The view controller then stored the closure in a property on the view, creating a retain cycle.

What is the retain graph of your program?

Here's the retain graph of your program:

retain graph showing doSomething referencing dog

There are no retain cycles in this graph.

Upvotes: 3

Crocobag
Crocobag

Reputation: 2340

Here is another "example" that runs outside of playground to showcase a retain cycle with a closure:

class ClosureClassStrongRefCycle {

    let str : String

    lazy var printStr: () -> () = { // self is captured
        print("\(self.str)")
    }

    init(str: String) {
        self.str = str
    }

    deinit {
        print("ClosureClassStrongRefCycle is being deallocated")
    } 
}

Now when you call your class like this :

 do {

      let myClassWithClosureStrongCycle = ClosureClassStrongRefCycle(str: "closure class strong cycle")
      myClassWithClosureStrongCycle.printStr()

 } // 'myClassWithClosureStrongCycle' is NEVER deallocated -> Strong reference cycle between this class and it's closure

the instance of ClosureClassStrongRefCycle retains itself because the closure within it retains self

finally if you want to get rid of the retain cycle, you can add unowned self like this :

lazy var printStr: () -> () = { [unowned self] in // Unowned copy of self inside printStr
    print("\(self.str)")
}

Upvotes: 0

Related Questions