Dániel Nagy
Dániel Nagy

Reputation: 12015

Function stored as variable can cause reference cycle?

If I have a class, that stores an others class function as a variable, can that cause reference cycle?

For example

class ClassA {

    var i = 0

    func incrementI() {
        i++
    }
}

class ClassB {

    private var function: () -> ()

    init(function: () -> ()) {
        self.function = function
    }

    func someFunc() {
        function()
    }
}

I cannot store in ClassB the variable function as weak or unowned, so can this cause reference cycle? Or this just reflects my poor understanding on reference cycles/function?

Upvotes: 1

Views: 90

Answers (1)

Sandy Chapman
Sandy Chapman

Reputation: 11341

Reference cycles are exactly what they're name suggests. One object references another which references the original object. These can be made of two or more objects.

Why it's easy to make reference cycles using blocks or closures is because capturing self in a closure means the closure object has a reference to self. If self also has a reference to the closure than you have a reference cycle. I think your example is safe because you're passing the closure in ClassB's init which means that that closure shouldn't be able to have a reference to a non-existent object.

If your example took function as a method, then you'd have an issue as you could do this:

class ClassB {

    ...

    func setSomeFunc(function : ()->()) {
        self.function = function
    }

    func printSomething() {
        print("Something")
    }
}

...

func test() {
    var x1 = {
        print("Do Nothing")
    }
    var b : ClassB = ClassB(x1)
    var x2 = {
        b.printSomething()
    }
    b.setSomeFunc(x2)
}

In this example, we create x1 which has no external references.

[x1]

Then we create b with a reference to x1. This reference is added in ClassB's init.

[b]->[x1]

Then we create x2 with a reference to b. b is captured in the x2 closure which means that it will hold a strong reference to b.

 [x2]->[b]->[x1]

We now assign a new function, x2, to b. This will break the reference from b to x1...

[x2]->[b]-x->[x1]

and replace it with a reference to x2.

[x2]->[b]-\    [x1]
 /\       |
  \-------/

As you can see, we now have a circular (or cyclical) reference. The only way to now break that reference is to set b's function member to reference something else, or to set x2's captured b value to something else.

Upvotes: 1

Related Questions